Compare commits
56 Commits
add/new-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 | ||
|
|
98f88916b8 | ||
|
|
ed347e879e | ||
|
|
ea49b887ab | ||
|
|
4de1b443cf |
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
@@ -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
|
||||
7
.github/workflows/ci.yml
vendored
@@ -1,9 +1,6 @@
|
||||
name: CI
|
||||
name: Untrusted CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
|
||||
pull_request:
|
||||
merge_group:
|
||||
workflow_dispatch:
|
||||
@@ -25,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: |
|
||||
|
||||
66
.github/workflows/trusted_ci.yml
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
name: Trusted CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: "recursive"
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: "temurin"
|
||||
java-version: "17"
|
||||
|
||||
- name: Setup Gradle
|
||||
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: |
|
||||
echo 'org.gradle.parallel=true' >> gradle.properties
|
||||
echo 'org.gradle.vfs.watch=true' >> gradle.properties
|
||||
echo 'org.gradle.jvmargs=-Xmx2048m' >> gradle.properties
|
||||
sed -i 's/org.gradle.unsafe.configuration-cache=true//g' gradle.properties
|
||||
./gradlew zipRelease
|
||||
./gradlew zipDebug
|
||||
|
||||
- name: Prepare artifact
|
||||
if: success()
|
||||
id: prepareArtifact
|
||||
run: |
|
||||
releaseName=`ls module/build/outputs/release/ReZygisk-v*-release.zip | awk -F '(/|.zip)' '{print $5}'` && echo "releaseName=$releaseName" >> $GITHUB_OUTPUT
|
||||
debugName=`ls module/build/outputs/release/ReZygisk-v*-debug.zip | awk -F '(/|.zip)' '{print $5}'` && echo "debugName=$debugName" >> $GITHUB_OUTPUT
|
||||
unzip module/build/outputs/release/ReZygisk-v*-release.zip -d zksu-release
|
||||
unzip module/build/outputs/release/ReZygisk-v*-debug.zip -d zksu-debug
|
||||
|
||||
- name: Upload release
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ steps.prepareArtifact.outputs.releaseName }}
|
||||
path: "./zksu-release/*"
|
||||
|
||||
- name: Upload debug
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ steps.prepareArtifact.outputs.debugName }}
|
||||
path: "./zksu-debug/*"
|
||||
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
|
||||
|
||||
22
TRANSLATOR.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# 🌎 Translators
|
||||
- **ar_EG** by [@ZG089](https://github.com/ZG089)
|
||||
- **de_DE** by [@Blazzycrafer](https://github.com/Blazzycrafter)
|
||||
- **en_US** by [@PerformanC (The PerformanC Organization)](https://github.com/PerformanC)
|
||||
- **es_AR** by [@Flopster101](https://github.com/Flopster101)
|
||||
- **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) & [@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 [@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)
|
||||
- **zh_TW** by [@Meltartica](https://github.com/Meltartica)
|
||||
|
||||
> [!NOTE]
|
||||
> Want to add your translation? Go to [Crowdin](https://crowdin.com/project/rezygisk) and translate!
|
||||
@@ -18,7 +18,7 @@ fun String.execute(currentWorkingDir: File = file("./")): String {
|
||||
val gitCommitCount = "git rev-list HEAD --count".execute().toInt()
|
||||
val gitCommitHash = "git rev-parse --verify --short HEAD".execute()
|
||||
|
||||
val moduleId by extra("zygisksu")
|
||||
val moduleId by extra("rezygisk")
|
||||
val moduleName by extra("ReZygisk")
|
||||
val verName by extra("v1.0.0")
|
||||
val verCode by extra(gitCommitCount)
|
||||
@@ -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]);
|
||||
}
|
||||
|
||||
free(info->modules->modules);
|
||||
for (size_t i = 0; i < info->modules.modules_count; i++) {
|
||||
free(info->modules.modules[i]);
|
||||
}
|
||||
|
||||
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);
|
||||
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,43 +404,22 @@ 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) {
|
||||
img->bias = phdr[i].p_vaddr - phdr[i].p_offset;
|
||||
bias_calculated = true;
|
||||
if (!bias_calculated) for (int i = 0; i < img->header->e_phnum; ++i) {
|
||||
if (phdr[i].p_type != PT_LOAD) continue;
|
||||
|
||||
LOGI("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);
|
||||
img->bias = phdr[i].p_vaddr - phdr[i].p_offset;
|
||||
bias_calculated = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)",
|
||||
name, hash, bloom_idx, (unsigned long)mask, (unsigned long)bloom_word);
|
||||
/* INFO: Very loggy -- generates too much noise. GNU is rarely used for Zygisk context. */
|
||||
/* LOGW("Symbol '%s' (hash %u) filtered out by GNU Bloom Filter (idx %zu, mask 0x%lx, word 0x%lx)",
|
||||
name, hash, bloom_idx, (unsigned long)mask, (unsigned long)bloom_word);
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -94,14 +138,14 @@ char *read_string(int fd) {
|
||||
return buf;
|
||||
}
|
||||
|
||||
#define write_func(type) \
|
||||
ssize_t write_## type(int fd, type val) { \
|
||||
return write(fd, &val, sizeof(type)); \
|
||||
#define write_func(type) \
|
||||
ssize_t write_## type(int fd, type val) { \
|
||||
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)); \
|
||||
#define read_func(type) \
|
||||
ssize_t read_## type(int fd, type *val) { \
|
||||
return read_loop(fd, val, sizeof(type)); \
|
||||
}
|
||||
|
||||
write_func(uint8_t)
|
||||
|
||||
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
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
@@ -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);
|
||||
purge_unused_memory();
|
||||
|
||||
pdg_unprotect();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
pdg_unprotect();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
LOGD("Found so path for %p: %s", lib_memory, get_path(found));
|
||||
|
||||
char path[PATH_MAX];
|
||||
if (get_path(found) == NULL) {
|
||||
LOGE("Failed to get path for %p", found);
|
||||
|
||||
return false;
|
||||
}
|
||||
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();
|
||||
for (int i = 0; i < nfds; i++) {
|
||||
((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 };
|
||||
uint8_t cmd;
|
||||
ssize_t nread = TEMP_FAILURE_RETRY(read(monitor_sock_fd, &cmd, sizeof(cmd)));
|
||||
if (nread == -1) {
|
||||
PLOGE("read socket");
|
||||
|
||||
size_t nread;
|
||||
|
||||
again:
|
||||
nread = read(monitor_sock_fd, &msg, sizeof(msg));
|
||||
if ((int)nread == -1) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) goto again;
|
||||
|
||||
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;
|
||||
}
|
||||
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);
|
||||
|
||||
@@ -355,7 +388,7 @@ void rezygiskd_listener_callback() {
|
||||
case SYSTEM_SERVER_STARTED: {
|
||||
LOGD("system server started, mounting prop");
|
||||
|
||||
if (mount(PROP_PATH, "/data/adb/modules/zygisksu/module.prop", NULL, MS_BIND, NULL) == -1) {
|
||||
if (mount(PROP_PATH, "/data/adb/modules/rezygisk/module.prop", NULL, MS_BIND, NULL) == -1) {
|
||||
PLOGE("failed to mount prop");
|
||||
}
|
||||
|
||||
@@ -363,8 +396,6 @@ void rezygiskd_listener_callback() {
|
||||
}
|
||||
}
|
||||
|
||||
if (msg_data) free(msg_data);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -405,7 +436,7 @@ static bool ensure_daemon_created(bool is_64bit) {
|
||||
if (is_64bit || (!is_64bit && !status64.supported)) {
|
||||
LOGD("new zygote started.");
|
||||
|
||||
umount2("/data/adb/modules/zygisksu/module.prop", MNT_DETACH);
|
||||
umount2("/data/adb/modules/rezygisk/module.prop", MNT_DETACH);
|
||||
}
|
||||
|
||||
if (status->daemon_pid != -1) {
|
||||
@@ -439,51 +470,49 @@ static bool ensure_daemon_created(bool is_64bit) {
|
||||
return true;
|
||||
}
|
||||
|
||||
#define CHECK_DAEMON_EXIT(abi) \
|
||||
if (status##abi.supported && pid == status##abi.daemon_pid) { \
|
||||
char status_str[64]; \
|
||||
parse_status(sigchld_status, status_str, sizeof(status_str)); \
|
||||
\
|
||||
LOGW("daemon" #abi " pid %d exited: %s", pid, status_str); \
|
||||
status##abi.daemon_running = false; \
|
||||
\
|
||||
if (!status##abi.daemon_error_info) { \
|
||||
status##abi.daemon_error_info = (char *)malloc(strlen(status_str) + 1); \
|
||||
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; \
|
||||
#define CHECK_DAEMON_EXIT(abi) \
|
||||
if (status##abi.supported && pid == status##abi.daemon_pid) { \
|
||||
char status_str[64]; \
|
||||
parse_status(sigchld_status, status_str, sizeof(status_str)); \
|
||||
\
|
||||
LOGW("daemon" #abi " pid %d exited: %s", pid, status_str); \
|
||||
status##abi.daemon_running = false; \
|
||||
\
|
||||
if (!status##abi.daemon_error_info) { \
|
||||
status##abi.daemon_error_info = strdup(status_str); \
|
||||
if (!status##abi.daemon_error_info) { \
|
||||
LOGE("malloc daemon" #abi " error info failed"); \
|
||||
\
|
||||
return; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
continue; \
|
||||
}
|
||||
|
||||
#define PRE_INJECT(abi, is_64) \
|
||||
if (strcmp(program, "/system/bin/app_process" # abi) == 0) { \
|
||||
tracer = "./bin/zygisk-ptrace" # abi; \
|
||||
\
|
||||
if (should_stop_inject ## abi()) { \
|
||||
LOGW("zygote" # abi " restart too much times, stop injecting"); \
|
||||
\
|
||||
tracing_state = STOPPING; \
|
||||
memcpy(monitor_stop_reason, "zygote crashed", sizeof("zygote crashed")); \
|
||||
ptrace(PTRACE_INTERRUPT, 1, 0, 0); \
|
||||
\
|
||||
break; \
|
||||
} \
|
||||
if (!ensure_daemon_created(is_64)) { \
|
||||
LOGW("daemon" #abi " not running, stop injecting"); \
|
||||
\
|
||||
tracing_state = STOPPING; \
|
||||
memcpy(monitor_stop_reason, "daemon not running", sizeof("daemon not running")); \
|
||||
ptrace(PTRACE_INTERRUPT, 1, 0, 0); \
|
||||
\
|
||||
break; \
|
||||
} \
|
||||
#define PRE_INJECT(abi, is_64) \
|
||||
if (strcmp(program, "/system/bin/app_process" # abi) == 0) { \
|
||||
tracer = "./bin/zygisk-ptrace" # abi; \
|
||||
\
|
||||
if (should_stop_inject ## abi()) { \
|
||||
LOGW("Zygote" # abi " restart too much times, stop injecting"); \
|
||||
\
|
||||
tracing_state = STOPPING; \
|
||||
strcpy(monitor_stop_reason, "Zygote crashed"); \
|
||||
ptrace(PTRACE_INTERRUPT, 1, 0, 0); \
|
||||
\
|
||||
break; \
|
||||
} \
|
||||
\
|
||||
if (!ensure_daemon_created(is_64)) { \
|
||||
LOGW("ReZygiskd " #abi "-bit not running, stop injecting"); \
|
||||
\
|
||||
tracing_state = STOPPING; \
|
||||
strcpy(monitor_stop_reason, "ReZygiskd not running"); \
|
||||
ptrace(PTRACE_INTERRUPT, 1, 0, 0); \
|
||||
\
|
||||
break; \
|
||||
} \
|
||||
}
|
||||
|
||||
int sigchld_signal_fd;
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -800,7 +885,7 @@ static bool prepare_environment() {
|
||||
/* INFO: We need to create the file first, otherwise the mount will fail */
|
||||
close(open(PROP_PATH, O_WRONLY | O_CREAT | O_TRUNC, 0644));
|
||||
|
||||
FILE *orig_prop = fopen("/data/adb/modules/zygisksu/module.prop", "r");
|
||||
FILE *orig_prop = fopen("/data/adb/modules/rezygisk/module.prop", "r");
|
||||
if (orig_prop == NULL) {
|
||||
PLOGE("failed to open orig prop");
|
||||
|
||||
@@ -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");
|
||||
|
||||
dlopen_addr = find_func_addr(local_map, map, "/system/bin/linker", "__dl_dlopen");
|
||||
#ifdef __LP64__
|
||||
dlopen_addr = find_func_addr(local_map, map, "/system/bin/linker64", "__dl_dlopen");
|
||||
#else
|
||||
dlopen_addr = find_func_addr(local_map, map, "/system/bin/linker", "__dl_dlopen");
|
||||
#endif
|
||||
if (dlopen_addr == NULL) {
|
||||
PLOGE("Find __dl_dlopen");
|
||||
|
||||
@@ -198,16 +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");
|
||||
|
||||
dlerror_addr = find_func_addr(local_map, map, "/system/bin/linker", "__dl_dlerror");
|
||||
#ifdef __LP64__
|
||||
dlerror_addr = find_func_addr(local_map, map, "/system/bin/linker64", "__dl_dlerror");
|
||||
#else
|
||||
dlerror_addr = find_func_addr(local_map, map, "/system/bin/linker", "__dl_dlerror");
|
||||
#endif
|
||||
if (dlerror_addr == NULL) {
|
||||
LOGE("Find __dl_dlerror");
|
||||
|
||||
@@ -225,19 +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");
|
||||
|
||||
dlsym_addr = find_func_addr(local_map, map, "/system/bin/linker", "__dl_dlsym");
|
||||
#ifdef __LP64__
|
||||
dlsym_addr = find_func_addr(local_map, map, "/system/bin/linker64", "__dl_dlsym");
|
||||
#else
|
||||
dlsym_addr = find_func_addr(local_map, map, "/system/bin/linker", "__dl_dlsym");
|
||||
#endif
|
||||
if (dlsym_addr == NULL) {
|
||||
LOGE("find __dl_dlsym");
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -85,12 +85,14 @@ androidComponents.onVariants { variant ->
|
||||
into("lib") {
|
||||
from(project(":loader").layout.buildDirectory.file("intermediates/stripped_native_libs/$variantLowered/out/lib"))
|
||||
}
|
||||
into("webroot") {
|
||||
from("${rootProject.projectDir}/webroot")
|
||||
}
|
||||
|
||||
val root = moduleDir.get()
|
||||
|
||||
doLast {
|
||||
if (file("private_key").exists()) {
|
||||
println("=== Guards the peace of Machikado ===")
|
||||
val privateKey = file("private_key").readBytes()
|
||||
val publicKey = file("public_key").readBytes()
|
||||
val namedSpec = NamedParameterSpec("ed25519")
|
||||
@@ -112,6 +114,35 @@ androidComponents.onVariants { variant ->
|
||||
}
|
||||
}
|
||||
|
||||
/* INFO: Misaki is the file that holds signed hash of
|
||||
all files of ReZygisk module, to ensure the
|
||||
zip (runtime and non-runtime) files hasn't
|
||||
been tampered with.
|
||||
*/
|
||||
fun misakiSign() {
|
||||
sig.initSign(privKey)
|
||||
|
||||
val filesToProcess = TreeSet<File> { f1, f2 ->
|
||||
f1.path.replace("\\", "/")
|
||||
.compareTo(f2.path.replace("\\", "/"))
|
||||
}
|
||||
|
||||
root.asFile.walkTopDown().forEach { file ->
|
||||
if (!file.isFile) return@forEach
|
||||
|
||||
val fileName = file.name
|
||||
if (fileName == "misaki.sig") return@forEach
|
||||
|
||||
filesToProcess.add(file)
|
||||
}
|
||||
|
||||
filesToProcess.forEach { file -> file.sha(file) }
|
||||
|
||||
val misakiSignatureFile = root.file("misaki.sig").asFile
|
||||
misakiSignatureFile.writeBytes(sig.sign())
|
||||
misakiSignatureFile.appendBytes(publicKey)
|
||||
}
|
||||
|
||||
fun getSign(name: String, abi: String, is64Bit: Boolean) {
|
||||
val set = TreeSet<Pair<File, File?>> { o1, o2 ->
|
||||
o1.first.path.replace("\\", "/")
|
||||
@@ -143,6 +174,7 @@ androidComponents.onVariants { variant ->
|
||||
root.file("bin/$abi/zygiskd").asFile
|
||||
)
|
||||
)
|
||||
|
||||
sig.initSign(privKey)
|
||||
set.forEach { it.first.sha(it.second) }
|
||||
val signFile = root.file(name).asFile
|
||||
@@ -150,11 +182,32 @@ androidComponents.onVariants { variant ->
|
||||
signFile.appendBytes(publicKey)
|
||||
}
|
||||
|
||||
/* INFO: Machikado is the name of files that holds signed hash of
|
||||
all runtime files of ReZygisk module, to ensure the
|
||||
runtime files hasn't been tampered with.
|
||||
*/
|
||||
println("=== Guards the peace of Machikado ===")
|
||||
|
||||
getSign("machikado.arm64", "arm64-v8a", true)
|
||||
getSign("machikado.arm", "armeabi-v7a", false)
|
||||
|
||||
getSign("machikado.x86_64", "x86_64", true)
|
||||
getSign("machikado.x86", "x86", false)
|
||||
|
||||
fileTree(moduleDir).visit {
|
||||
if (isDirectory) return@visit
|
||||
|
||||
val md = MessageDigest.getInstance("SHA-256")
|
||||
file.forEachBlock(4096) { bytes, size ->
|
||||
md.update(bytes, 0, size)
|
||||
}
|
||||
|
||||
file(file.path + ".sha256").writeText(Hex.encodeHexString(md.digest()))
|
||||
}
|
||||
|
||||
println("=== At the kitsune's wedding ===")
|
||||
|
||||
misakiSign()
|
||||
} else {
|
||||
println("no private_key found, this build will not be signed")
|
||||
|
||||
@@ -163,15 +216,19 @@ androidComponents.onVariants { variant ->
|
||||
|
||||
root.file("machikado.x86_64").asFile.createNewFile()
|
||||
root.file("machikado.x86").asFile.createNewFile()
|
||||
}
|
||||
|
||||
fileTree(moduleDir).visit {
|
||||
if (isDirectory) return@visit
|
||||
val md = MessageDigest.getInstance("SHA-256")
|
||||
file.forEachBlock(4096) { bytes, size ->
|
||||
md.update(bytes, 0, size)
|
||||
fileTree(moduleDir).visit {
|
||||
if (isDirectory) return@visit
|
||||
|
||||
val md = MessageDigest.getInstance("SHA-256")
|
||||
file.forEachBlock(4096) { bytes, size ->
|
||||
md.update(bytes, 0, size)
|
||||
}
|
||||
|
||||
file(file.path + ".sha256").writeText(Hex.encodeHexString(md.digest()))
|
||||
}
|
||||
file(file.path + ".sha256").writeText(Hex.encodeHexString(md.digest()))
|
||||
|
||||
root.file("misaki.sig").asFile.createNewFile()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,8 +107,13 @@ extract "$ZIPFILE" 'uninstall.sh' "$MODPATH"
|
||||
mv "$TMPDIR/sepolicy.rule" "$MODPATH"
|
||||
|
||||
mkdir "$MODPATH/bin"
|
||||
mkdir "$MODPATH/webroot"
|
||||
|
||||
CPU_ABIS=$(getprop ro.product.cpu.abilist)
|
||||
ui_print "- Extracting webroot"
|
||||
unzip -o "$ZIPFILE" "webroot/*" -x "*.sha256" -d "$MODPATH"
|
||||
|
||||
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 *
|
||||
|
||||
661
webroot/LICENSE
Normal file
@@ -0,0 +1,661 @@
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
1
webroot/assets/action.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M240-160q-33 0-56.5-23.5T160-240q0-33 23.5-56.5T240-320q33 0 56.5 23.5T320-240q0 33-23.5 56.5T240-160Zm240 0q-33 0-56.5-23.5T400-240q0-33 23.5-56.5T480-320q33 0 56.5 23.5T560-240q0 33-23.5 56.5T480-160Zm240 0q-33 0-56.5-23.5T640-240q0-33 23.5-56.5T720-320q33 0 56.5 23.5T800-240q0 33-23.5 56.5T720-160ZM240-400q-33 0-56.5-23.5T160-480q0-33 23.5-56.5T240-560q33 0 56.5 23.5T320-480q0 33-23.5 56.5T240-400Zm240 0q-33 0-56.5-23.5T400-480q0-33 23.5-56.5T480-560q33 0 56.5 23.5T560-480q0 33-23.5 56.5T480-400Zm240 0q-33 0-56.5-23.5T640-480q0-33 23.5-56.5T720-560q33 0 56.5 23.5T800-480q0 33-23.5 56.5T720-400ZM240-640q-33 0-56.5-23.5T160-720q0-33 23.5-56.5T240-800q33 0 56.5 23.5T320-720q0 33-23.5 56.5T240-640Zm240 0q-33 0-56.5-23.5T400-720q0-33 23.5-56.5T480-800q33 0 56.5 23.5T560-720q0 33-23.5 56.5T480-640Zm240 0q-33 0-56.5-23.5T640-720q0-33 23.5-56.5T720-800q33 0 56.5 23.5T800-720q0 33-23.5 56.5T720-640Z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
1
webroot/assets/back.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="36px" viewBox="0 -960 960 960" width="36px" fill="#e8eaed"><path d="m287-446.67 240 240L480-160 160-480l320-320 47 46.67-240 240h513v66.66H287Z"/></svg>
|
||||
|
After Width: | Height: | Size: 200 B |
1
webroot/assets/close.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="40px" viewBox="0 -960 960 960" width="40px" fill="#e8eaed"><path d="m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z"/></svg>
|
||||
|
After Width: | Height: | Size: 222 B |
1
webroot/assets/content.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="40px" viewBox="0 -960 960 960" width="40px" fill="#e8eaed"><path d="M320-213.33q-27 0-46.83-19.84Q253.33-253 253.33-280v-533.33q0-27 19.84-46.84Q293-880 320-880h413.33q27 0 46.84 19.83Q800-840.33 800-813.33V-280q0 27-19.83 46.83-19.84 19.84-46.84 19.84H320Zm0-66.67h413.33v-533.33H320V-280ZM186.67-80q-27 0-46.84-19.83Q120-119.67 120-146.67v-600h66.67v600h480V-80h-480ZM320-280v-533.33V-280Z"/></svg>
|
||||
|
After Width: | Height: | Size: 448 B |
1
webroot/assets/delete.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="40px" viewBox="0 -960 960 960" width="40px" fill="#e8eaed"><path d="M267.33-120q-27.5 0-47.08-19.58-19.58-19.59-19.58-47.09V-740H160v-66.67h192V-840h256v33.33h192V-740h-40.67v553.33q0 27-19.83 46.84Q719.67-120 692.67-120H267.33Zm425.34-620H267.33v553.33h425.34V-740Zm-328 469.33h66.66v-386h-66.66v386Zm164 0h66.66v-386h-66.66v386ZM267.33-740v553.33V-740Z"/></svg>
|
||||
|
After Width: | Height: | Size: 411 B |
1
webroot/assets/ec-icon.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="60px" viewBox="0 -960 960 960" width="60px" fill="#ba1a1a"><path d="M92-120q-9 0-15.65-4.13Q69.7-128.25 66-135q-4.17-6.6-4.58-14.3Q61-157 66-165l388-670q5-8 11.5-11.5T480-850q8 0 14.5 3.5T506-835l388 670q5 8 4.58 15.7-.41 7.7-4.58 14.3-3.7 6.75-10.35 10.87Q877-120 868-120H92Zm392.18-117q12.82 0 21.32-8.68 8.5-8.67 8.5-21.5 0-12.82-8.68-21.32-8.67-8.5-21.5-8.5-12.82 0-21.32 8.68-8.5 8.67-8.5 21.5 0 12.82 8.68 21.32 8.67 8.5 21.5 8.5Zm0-111q12.82 0 21.32-8.63 8.5-8.62 8.5-21.37v-164q0-12.75-8.68-21.38-8.67-8.62-21.5-8.62-12.82 0-21.32 8.62-8.5 8.63-8.5 21.38v164q0 12.75 8.68 21.37 8.67 8.63 21.5 8.63Z"/></svg>
|
||||
|
After Width: | Height: | Size: 663 B |
1
webroot/assets/error.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M480-280q17 0 28.5-11.5T520-320q0-17-11.5-28.5T480-360q-17 0-28.5 11.5T440-320q0 17 11.5 28.5T480-280Zm-40-160h80v-240h-80v240Zm40 360q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>
|
||||
|
After Width: | Height: | Size: 537 B |
1
webroot/assets/expand.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="m480-340 180-180-57-56-123 123-123-123-57 56 180 180Zm0 260q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>
|
||||
|
After Width: | Height: | Size: 462 B |
1
webroot/assets/home.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M240-200h120v-240h240v240h120v-360L480-740 240-560v360Zm-80 80v-480l320-240 320 240v480H520v-240h-80v240H160Zm320-350Z"/></svg>
|
||||
|
After Width: | Height: | Size: 243 B |
1
webroot/assets/mark.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q54 0 104-17.5t92-50.5L228-676q-33 42-50.5 92T160-480q0 134 93 227t227 93Zm252-124q33-42 50.5-92T800-480q0-134-93-227t-227-93q-54 0-104 17.5T284-732l448 448Z"/></svg>
|
||||
|
After Width: | Height: | Size: 476 B |
1
webroot/assets/module.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M627-520h133v-160H627v160Zm-214 0h133v-160H413v160Zm-213 0h133v-160H200v160Zm0 240h133v-160H200v160Zm213 0h133v-160H413v160Zm214 0h133v-160H627v160Zm-507 0v-400q0-33 23.5-56.5T200-760h560q33 0 56.5 23.5T840-680v400q0 33-23.5 56.5T760-200H200q-33 0-56.5-23.5T120-280Z"/></svg>
|
||||
|
After Width: | Height: | Size: 391 B |
1
webroot/assets/settings.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="m370-80-16-128q-13-5-24.5-12T307-235l-119 50L78-375l103-78q-1-7-1-13.5v-27q0-6.5 1-13.5L78-585l110-190 119 50q11-8 23-15t24-12l16-128h220l16 128q13 5 24.5 12t22.5 15l119-50 110 190-103 78q1 7 1 13.5v27q0 6.5-2 13.5l103 78-110 190-118-50q-11 8-23 15t-24 12L590-80H370Zm70-80h79l14-106q31-8 57.5-23.5T639-327l99 41 39-68-86-65q5-14 7-29.5t2-31.5q0-16-2-31.5t-7-29.5l86-65-39-68-99 42q-22-23-48.5-38.5T533-694l-13-106h-79l-14 106q-31 8-57.5 23.5T321-633l-99-41-39 68 86 64q-5 15-7 30t-2 32q0 16 2 31t7 30l-86 65 39 68 99-42q22 23 48.5 38.5T427-266l13 106Zm42-180q58 0 99-41t41-99q0-58-41-99t-99-41q-59 0-99.5 41T342-480q0 58 40.5 99t99.5 41Zm-2-140Z"/></svg>
|
||||
|
After Width: | Height: | Size: 771 B |
3
webroot/assets/tick.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg class="brightc" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed">
|
||||
<path d="m424-296 282-282-56-56-226 226-114-114-56 56 170 170Zm56 216q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 483 B |
1
webroot/assets/warn.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M480-280q17 0 28.5-11.5T520-320q0-17-11.5-28.5T480-360q-17 0-28.5 11.5T440-320q0 17 11.5 28.5T480-280Zm-40-160h80v-240h-80v240Zm40 360q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>
|
||||
|
After Width: | Height: | Size: 537 B |
3
webroot/assets_light/action.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#2c2c2c">
|
||||
<path d="M240-160q-33 0-56.5-23.5T160-240q0-33 23.5-56.5T240-320q33 0 56.5 23.5T320-240q0 33-23.5 56.5T240-160Zm240 0q-33 0-56.5-23.5T400-240q0-33 23.5-56.5T480-320q33 0 56.5 23.5T560-240q0 33-23.5 56.5T480-160Zm240 0q-33 0-56.5-23.5T640-240q0-33 23.5-56.5T720-320q33 0 56.5 23.5T800-240q0 33-23.5 56.5T720-160ZM240-400q-33 0-56.5-23.5T160-480q0-33 23.5-56.5T240-560q33 0 56.5 23.5T320-480q0 33-23.5 56.5T240-400Zm240 0q-33 0-56.5-23.5T400-480q0-33 23.5-56.5T480-560q33 0 56.5 23.5T560-480q0 33-23.5 56.5T480-400Zm240 0q-33 0-56.5-23.5T640-480q0-33 23.5-56.5T720-560q33 0 56.5 23.5T800-480q0 33-23.5 56.5T720-400ZM240-640q-33 0-56.5-23.5T160-720q0-33 23.5-56.5T240-800q33 0 56.5 23.5T320-720q0 33-23.5 56.5T240-640Zm240 0q-33 0-56.5-23.5T400-720q0-33 23.5-56.5T480-800q33 0 56.5 23.5T560-720q0 33-23.5 56.5T480-640Zm240 0q-33 0-56.5-23.5T640-720q0-33 23.5-56.5T720-800q33 0 56.5 23.5T800-720q0 33-23.5 56.5T720-640Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
3
webroot/assets_light/home.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#2c2c2c">
|
||||
<path d="M240-200h120v-240h240v240h120v-360L480-740 240-560v360Zm-80 80v-480l320-240 320 240v480H520v-240h-80v240H160Zm320-350Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 247 B |
3
webroot/assets_light/module.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#2c2c2c">
|
||||
<path d="M627-520h133v-160H627v160Zm-214 0h133v-160H413v160Zm-213 0h133v-160H200v160Zm0 240h133v-160H200v160Zm213 0h133v-160H413v160Zm214 0h133v-160H627v160Zm-507 0v-400q0-33 23.5-56.5T200-760h560q33 0 56.5 23.5T840-680v400q0 33-23.5 56.5T760-200H200q-33 0-56.5-23.5T120-280Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 395 B |
3
webroot/assets_light/settings.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#2c2c2c">
|
||||
<path d="m370-80-16-128q-13-5-24.5-12T307-235l-119 50L78-375l103-78q-1-7-1-13.5v-27q0-6.5 1-13.5L78-585l110-190 119 50q11-8 23-15t24-12l16-128h220l16 128q13 5 24.5 12t22.5 15l119-50 110 190-103 78q1 7 1 13.5v27q0 6.5-2 13.5l103 78-110 190-118-50q-11 8-23 15t-24 12L590-80H370Zm70-80h79l14-106q31-8 57.5-23.5T639-327l99 41 39-68-86-65q5-14 7-29.5t2-31.5q0-16-2-31.5t-7-29.5l86-65-39-68-99 42q-22-23-48.5-38.5T533-694l-13-106h-79l-14 106q-31 8-57.5 23.5T321-633l-99-41-39 68 86 64q-5 15-7 30t-2 32q0 16 2 31t7 30l-86 65 39 68 99-42q22 23 48.5 38.5T427-266l13 106Zm42-180q58 0 99-41t41-99q0-58-41-99t-99-41q-59 0-99.5 41T342-480q0 58 40.5 99t99.5 41Zm-2-140Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 775 B |
56
webroot/css/error.css
Normal file
@@ -0,0 +1,56 @@
|
||||
.e-container {
|
||||
background-color: black;
|
||||
z-index: 30;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.e-main {
|
||||
background-color: black;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.e-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow: auto;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.e-bg {
|
||||
background-color: black;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.e-card {
|
||||
margin: 30px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.e-font {
|
||||
color: #ba1a1a;
|
||||
}
|
||||
|
||||
.e-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 120px;
|
||||
height: 40px;
|
||||
border-radius: 30px;
|
||||
background-color: #ba1a1a;
|
||||
color: black;
|
||||
transition: transform 0.1s ease-in-out;
|
||||
}
|
||||
|
||||
.e-button:active {
|
||||
transform: scale(90%)
|
||||
}
|
||||
62
webroot/css/icons.css
Normal file
@@ -0,0 +1,62 @@
|
||||
/* Dark */
|
||||
#ni_home {
|
||||
background: url(../assets/home.svg);
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
#ni_modules {
|
||||
background: url(../assets/module.svg);
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
#ni_actions {
|
||||
background: url(../assets/action.svg);
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
#ni_settings {
|
||||
background: url(../assets/settings.svg);
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
#ec_icon {
|
||||
background: url(../assets/ec-icon.svg);
|
||||
z-index: 40;
|
||||
}
|
||||
|
||||
/* Light */
|
||||
#ni_home.light {
|
||||
background: url(../assets_light/home.svg);
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
#ni_modules.light {
|
||||
background: url(../assets_light/module.svg);
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
#ni_actions.light {
|
||||
background: url(../assets_light/action.svg);
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
#ni_settings.light {
|
||||
background: url(../assets_light/settings.svg);
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
z-index: 10;
|
||||
}
|
||||
514
webroot/css/index.css
Normal file
@@ -0,0 +1,514 @@
|
||||
:root {
|
||||
--background: #181c20;
|
||||
--font: #fff;
|
||||
--desc: #c9c9c9;
|
||||
--bright: #8d1d19;
|
||||
--dim: #1d2327;
|
||||
--error: #8d1d19;
|
||||
--icon: #48565e;
|
||||
--icon-bc: #2b3338;
|
||||
--small-card: var(--icon-bc);
|
||||
--button: var(--background);
|
||||
--desktop-navbar: #252b31;
|
||||
--desktop-navicon: #333d42;
|
||||
/* Locked Color */
|
||||
--lock-desc: #c9c9c9;
|
||||
--lock: #fff;
|
||||
--font-family: 'Poppins', sans-serif;
|
||||
}
|
||||
|
||||
* {
|
||||
background-color: var(--background);
|
||||
color: var(--font);
|
||||
font-family: var(--font-family)
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
/* Components */
|
||||
.radios input[type=radio] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.radio input[type=radio] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.not_avaliable {
|
||||
position: fixed;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.2em;
|
||||
text-align: center;
|
||||
top: 0; left: 30px; right: 30px; bottom: 0;
|
||||
}
|
||||
|
||||
.load_screen {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background: var(--background);
|
||||
z-index: 10;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.full_screen {
|
||||
position: fixed;
|
||||
overflow-y: scroll;
|
||||
z-index: 10;
|
||||
top: 0; bottom: 0; left: 0; right: 0;
|
||||
transition: top 0.35s ease-in-out;
|
||||
}
|
||||
|
||||
.loader {
|
||||
border: 6px solid var(--icon-bc);
|
||||
border-top: 6px solid var(--icon);
|
||||
border-radius: 50%;
|
||||
width: 3em;
|
||||
height: 3em;
|
||||
animation: spin 0.8s linear infinite;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
position: fixed;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 20px 25px;
|
||||
font-size: 20px;
|
||||
border-bottom: #585b5d solid 1px;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.panel {
|
||||
display: none;
|
||||
animation: fade-out 0.2s;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
display: flex;
|
||||
position: fixed;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background-color: var(--dim);
|
||||
left: 0; right: 0; bottom: 0;
|
||||
padding: 25px 25px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.navtitle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: small;
|
||||
margin-top: 8px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.navhidden {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.navicon {
|
||||
background: var(--small-card);
|
||||
display: flex;
|
||||
width: 0;
|
||||
height: 30px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50px;
|
||||
animation: closeNav 0.15s;
|
||||
}
|
||||
|
||||
.card {
|
||||
margin: 0 15px 10px 15px;
|
||||
padding: 15px 15px;
|
||||
border-radius: 15px;
|
||||
color: var(--lock);
|
||||
}
|
||||
|
||||
.content {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.desc {
|
||||
color: var(--desc);
|
||||
}
|
||||
|
||||
.liste {
|
||||
justify-content: space-between;
|
||||
align-self: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.center {
|
||||
justify-content: center;
|
||||
align-self: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.list {
|
||||
max-height: 25px;
|
||||
overflow-y: hidden;
|
||||
transition: max-height 0.25s ease;
|
||||
}
|
||||
|
||||
.full {
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.expander {
|
||||
transform: rotate(0deg);
|
||||
transition: 0.2s ease-out;
|
||||
}
|
||||
|
||||
.button_list {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
column-gap: 20px;
|
||||
}
|
||||
|
||||
.button {
|
||||
padding: 1em 0 1em 0;
|
||||
border-radius: 30px;
|
||||
transition: transform 0.1s ease-in-out;
|
||||
background-color: var(--button);
|
||||
}
|
||||
|
||||
.button:active {
|
||||
transform: scale(90%)
|
||||
}
|
||||
|
||||
.small_card {
|
||||
background: var(--small-card);
|
||||
border-radius: 15px;
|
||||
padding: 5px 15px 0 15px;
|
||||
margin: 15px 15px 0 15px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.small_card_bg {
|
||||
background: var(--small-card);
|
||||
}
|
||||
|
||||
.icon_animation {
|
||||
transition: transform 0.05s ease-in-out;
|
||||
}
|
||||
|
||||
.icon_animation:active {
|
||||
transform: scale(80%)
|
||||
}
|
||||
|
||||
.element_animation {
|
||||
transition: transform 0.15s ease-in-out;
|
||||
}
|
||||
|
||||
.element_animation:active {
|
||||
transform: scale(95%)
|
||||
}
|
||||
|
||||
.card_animation {
|
||||
transition: transform 0.05s ease-in-out;
|
||||
}
|
||||
|
||||
.card_animation:active {
|
||||
transform: scale(95%)
|
||||
}
|
||||
|
||||
.errorh_textarea {
|
||||
resize: none;
|
||||
border: none;
|
||||
outline: none;
|
||||
font-size: 14px;
|
||||
padding-bottom: 25px;
|
||||
font-family: monospace, monospace;
|
||||
position: fixed;
|
||||
bottom: 7em;
|
||||
top: 15.2em;
|
||||
left: 1em;
|
||||
right: 1em;
|
||||
}
|
||||
|
||||
.errorh_button_container {
|
||||
position: fixed;
|
||||
top: 5.75em;
|
||||
left: 1em;
|
||||
right: 1em;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
column-gap: 1em;
|
||||
}
|
||||
|
||||
.errorh_button {
|
||||
border-radius: 25px;
|
||||
background: var(--small-card);
|
||||
display: flex;
|
||||
padding: 0.7em;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 1em;
|
||||
cursor: pointer;
|
||||
transition: transform 0.05s ease-in-out;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
#errorh_small_panel_header {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.errorh_button:active {
|
||||
transform: scale(80%)
|
||||
}
|
||||
|
||||
/* Card type */
|
||||
.bright {
|
||||
border: 5px solid var(--bright);
|
||||
background: var(--bright);
|
||||
}
|
||||
|
||||
.brightc {
|
||||
background: var(--bright);
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.dim {
|
||||
border: 5px solid var(--dim);
|
||||
background: var(--dim);
|
||||
}
|
||||
|
||||
.dimc {
|
||||
background: var(--dim);
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.nav_dimc {
|
||||
background: var(--dim);
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
/* Locked */
|
||||
.lock {
|
||||
color: var(--lock);
|
||||
}
|
||||
|
||||
.lockd {
|
||||
color: var(--lock-desc);
|
||||
}
|
||||
|
||||
/* Switch */
|
||||
.switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 60px;
|
||||
height: 34px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
background-color: var(--button);
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
}
|
||||
|
||||
.slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 23px;
|
||||
width: 23px;
|
||||
left: 6px;
|
||||
bottom: 6px;
|
||||
background-color: var(--desc);
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
}
|
||||
|
||||
input:checked + .slider:before {
|
||||
-webkit-transform: translateX(26px);
|
||||
-ms-transform: translateX(26px);
|
||||
transform: translateX(26px);
|
||||
}
|
||||
|
||||
/* Rounded sliders */
|
||||
.slider.round {
|
||||
border-radius: 34px;
|
||||
}
|
||||
|
||||
.slider.round:before {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
/* Responsive CSS */
|
||||
@media only screen and (max-width: 600px) {
|
||||
.button_list {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
column-gap: 20px;
|
||||
}
|
||||
.button {
|
||||
padding: 1em 0 1em 0;
|
||||
border-radius: 30px;
|
||||
margin: 0.6em 0 0.6em;
|
||||
transition: transform 0.15s ease-in-out;
|
||||
background-color: var(--button);
|
||||
}
|
||||
}
|
||||
|
||||
/* Show set */
|
||||
.panel.show {
|
||||
margin-bottom: 119px;
|
||||
display: block;
|
||||
animation: fade-in 0.2s;
|
||||
}
|
||||
|
||||
.panel.showback {
|
||||
margin-bottom: 119px;
|
||||
display: block;
|
||||
animation: fade-in 0.2s;
|
||||
z-index: 30;
|
||||
}
|
||||
|
||||
.navicon.show {
|
||||
width: 65px;
|
||||
animation: openNav 0.15s;
|
||||
}
|
||||
|
||||
@media (min-width:961px) {
|
||||
.navbar {
|
||||
background-color: var(--desktop-navbar);
|
||||
transform: scale(0.85);
|
||||
left: auto;
|
||||
right: auto;
|
||||
margin-bottom: 15px;
|
||||
border-radius: 60px;
|
||||
padding: 10px 10px;
|
||||
}
|
||||
.navtitle {
|
||||
display: none;
|
||||
}
|
||||
.navicon_support {
|
||||
border-radius: 60px;
|
||||
}
|
||||
#navbar_support_div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.navicon {
|
||||
background: var(--desktop-navicon);
|
||||
height: 65px;
|
||||
animation: closeNavDesktop 0.15s;
|
||||
}
|
||||
.navicon.show {
|
||||
animation: openNavDesktop 0.15s;
|
||||
}
|
||||
.nav_dimc {
|
||||
background: var(--desktop-navbar);
|
||||
}
|
||||
.errorh_button_container {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
.errorh_textarea {
|
||||
top: 11em;
|
||||
}
|
||||
.errorh_button {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Animation KeyFrames */
|
||||
@keyframes fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fade-out {
|
||||
from {
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@keyframes closeNav {
|
||||
0% {
|
||||
width: 65px;
|
||||
}
|
||||
75% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
width: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes openNav {
|
||||
0% {
|
||||
width: 0px;
|
||||
}
|
||||
75% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
width: 65px;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes closeNavDesktop {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
75% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
transform: scale(0);
|
||||
}
|
||||
}
|
||||
@keyframes openNavDesktop {
|
||||
0% {
|
||||
transform: scale(0);
|
||||
}
|
||||
75% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
4
webroot/fonts/header.css
Normal file
@@ -0,0 +1,4 @@
|
||||
@font-face {
|
||||
font-family: "Poppins";
|
||||
src: url("./poppins.ttf");
|
||||
}
|
||||
BIN
webroot/fonts/poppins.ttf
Normal file
381
webroot/index.html
Normal file
@@ -0,0 +1,381 @@
|
||||
<!DOCTYPE html>
|
||||
<html id="main_html" lang="en" dir="none">
|
||||
<head>
|
||||
<meta name="viewport" content="initial-scale=1, width=device-width" />
|
||||
<meta name="viewport" content="viewport-fit=cover" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<meta id="cache-navbar-previous" />
|
||||
<meta id="cache-page-small-previous" />
|
||||
<meta id="cache-fallback-open" />
|
||||
|
||||
<link rel="stylesheet" href="fonts/header.css">
|
||||
<link rel="stylesheet" href="css/index.css">
|
||||
<link rel="stylesheet" href="css/error.css">
|
||||
<link rel="stylesheet" href="css/icons.css">
|
||||
|
||||
<script>
|
||||
localStorage.setItem('/system/boot-time', String(Date.now()))
|
||||
</script>
|
||||
<script src="js/errorCatcher.js" type="module"></script>
|
||||
<script src="js/errorScreen.js" type="module"></script>
|
||||
<script src="js/browserRedirect.js" type="module"></script>
|
||||
<script src="js/smallPage/theme.js" type="module"></script>
|
||||
<script src="js/smallPage/language.js" type="module"></script>
|
||||
<script src="js/smallPage/errorHistory.js" type="module"></script>
|
||||
<script src="js/restoreError.js" type="module"></script>
|
||||
<script src="js/navbar.js" type="module"></script>
|
||||
<script src="js/monitorActions.js" type="module"></script>
|
||||
<script src="js/switcher/fontChanger.js" type="module"></script>
|
||||
<script src="js/main.js" type="module"></script>
|
||||
</head>
|
||||
<body id="main_body">
|
||||
<!-- INFO: Error screen -->
|
||||
<div id="fatal-error-screen" class="e-container" style="display: none;">
|
||||
<div class="e-bg e-wrap">
|
||||
<div class="e-bg e-card e-main">
|
||||
<img class="e-bg" src="./assets/ec-icon.svg" style="margin-bottom: 10px;"/>
|
||||
<div class="e-bg">
|
||||
Your WebUI is corrupt. It can't be trusted and may not work properly.
|
||||
</div>
|
||||
|
||||
<div class="e-bg" style="margin-top: 15px;">
|
||||
Please click the copy log button below and visit
|
||||
<div class="e-bg e-font" credit-link="t.me/performancorg">t.me/performancorg</div>
|
||||
for support.
|
||||
</div>
|
||||
|
||||
<div id="esc-copy-button" class="e-button" style="margin-top: 25px;">COPY LOG</div>
|
||||
<textarea id="esc-log" style="display: none;"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- INFO: Loading screen -->
|
||||
<div id="loading_screen" class="load_screen">
|
||||
<div id="backport_errorh" class="loader"></div>
|
||||
</div>
|
||||
|
||||
<!-- INFO: Home page -->
|
||||
<div id="panel_home" class="panel">
|
||||
<div class="header">ReZygisk</div>
|
||||
<div style="padding: 45px 0px;"></div>
|
||||
|
||||
<!-- INFO: Status card -->
|
||||
<div id="info_card" class="bright card list">
|
||||
<div class="brightc" style="display: flex; align-items: center;">
|
||||
<div id="rezygisk_icon_state" class="brightc" style="margin-bottom: 1px;">
|
||||
<img class="brightc" src="assets/mark.svg">
|
||||
</div>
|
||||
<div id="rezygisk_state" class="brightc content lock" style="font-size: 1.2em; padding-bottom: 4px; padding-left: 5px;">
|
||||
Unknown
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- INFO: Info card -->
|
||||
<div class="dim card">
|
||||
<div class="dimc" style="margin-bottom: 14px;">
|
||||
<div class="dimc" style="font-size: 0.8em">ROM / Kernel</div>
|
||||
</div>
|
||||
|
||||
<div id="android_version" class="dimc">
|
||||
<div class="dimc" style="font-size: 1.1em;">Android</div>
|
||||
<div class="dimc desc" id="android_version_div" style="font-size: 0.9em; line-height: 1.05em;">Unknown</div>
|
||||
</div>
|
||||
<div id="kernel_version" class="dimc" style="margin-top: 4px;">
|
||||
<div class="dimc" style="font-size: 1.1em;">Kernel</div>
|
||||
<div class="dimc desc" id="kernel_version_div" style="font-size: 0.9em; line-height: 1.05em;">Unknown</div>
|
||||
</div>
|
||||
|
||||
<div class="dimc" style="margin-top: 18px; margin-bottom: 14px;">
|
||||
<div class="dimc" style="font-size: 0.8em;">Root / ReZygisk</div>
|
||||
</div>
|
||||
|
||||
<div class="dimc content">
|
||||
<div id="version_info_title" class="dimc" style="font-size: 1.1em;">Version</div>
|
||||
<div class="dimc desc" id="version" style="font-size: 0.9em; line-height: 1.05em;">Unknown</div>
|
||||
</div>
|
||||
<div class="dimc content">
|
||||
<div id="root_info_title" class="dimc" style="font-size: 1.1em;">Root Implementation</div>
|
||||
<div class="dimc desc" id="root_impl" style="font-size: 0.9em; line-height: 1.05em;">Unknown</div>
|
||||
</div>
|
||||
|
||||
<div class="dimc" style="margin-top: 12px; margin-bottom: 14px;">
|
||||
<div class="dimc" style="font-size: 0.8em">Zygotes</div>
|
||||
</div>
|
||||
|
||||
<div id="zygote32" class="dimc content">
|
||||
<div class="dimc" style="font-size: 1.1em;">Zygote32</div>
|
||||
<div class="dimc desc" id="zygote32_status" style="font-size: 0.9em; line-height: 1.05em;">Unknown</div>
|
||||
</div>
|
||||
<div id="zygote64" class="dimc">
|
||||
<div class="dimc" style="font-size: 1.1em;">Zygote64</div>
|
||||
<div class="dimc desc" id="zygote64_status" style="font-size: 0.9em; line-height: 1.05em;">Unknown</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- INFO: Module page -->
|
||||
<div id="panel_modules" class="panel">
|
||||
<div id="panel_modules_header" class="header">Modules</div>
|
||||
<div style="padding: 45px 0px;"></div>
|
||||
<div id="modules_list" style="width: 100%; height: fit-content; display: grid;">
|
||||
<div id="modules_list_not_avaliable" class="not_avaliable">
|
||||
No modules using Zygisk here.
|
||||
</div>
|
||||
<!-- N/A -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- INFO: Action page -->
|
||||
<div id="panel_actions" class="panel">
|
||||
<div id="panel_actions_header" class="header">Actions</div>
|
||||
<div style="padding: 45px 0px;"></div>
|
||||
<!-- INFO: Settings card body (monitor) -->
|
||||
<div id="monitor" class="dimc" style="display: block; margin: 0 15px 0 15px; border-radius: 20px;">
|
||||
<div class="dimc liste" style="padding: 30px 22px 30px; border-radius: 20px;">
|
||||
<div id="monitor_title" class="dimc" style="font-size: 1.22em;">Monitor</div>
|
||||
<div id="monitor_status" class="dimc" style="font-size: 1.1em;">Unknown</div>
|
||||
</div>
|
||||
|
||||
<div class="small_card_bg" style="padding: 25px 20px 25px 20px; border-radius: 20px;">
|
||||
<div class="small_card_bg button_list">
|
||||
<div id="monitor_stop_button" class="center button">Stop</div>
|
||||
<div id="monitor_start_button" class="center button">Start</div>
|
||||
<div id="monitor_pause_button" class="center button">Pause</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- INFO: Settings page -->
|
||||
<div id="panel_settings" class="panel">
|
||||
<div id="panel_settings_header" class="header">Settings</div>
|
||||
<div style="padding: 40px 0px;"></div>
|
||||
<!-- INFO: Enable system font option -->
|
||||
<div class="small_card dimc" style="margin-top: 15px; justify-content: space-between; display: flex; align-items: center;">
|
||||
<div class="dimc" style="display: inline-block; width: 80%;">
|
||||
<div id="sys_font_option_title" class="dimc content" style="font-size: 1.1em; padding-left: 5px; padding-top: 6px; padding-bottom: 2px;">
|
||||
Enable system font
|
||||
</div>
|
||||
<div id="sys_font_option_desc" class="dimc desc" style="font-size: 0.9em; padding-left: 5px; padding-bottom: 6px;">
|
||||
This option will enable system font in current WebUI. NOTE: May not be compatible with FlipFont
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label class="switch dimc">
|
||||
<input id="font_switcher" type="checkbox">
|
||||
<span class="slider round"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- INFO: Enable light theme option -->
|
||||
<div id="theme_page_toggle" class="small_card dimc card_animation" style="margin-top: 15px; justify-content: space-between; display: flex; align-items: center;">
|
||||
<div class="dimc" style="display: inline-block;">
|
||||
<div id="sys_theme_option_title" class="dimc content" style="font-size: 1.1em; padding-left: 5px; padding-top: 6px; padding-bottom: 2px;">
|
||||
System theme
|
||||
</div>
|
||||
<div id="sys_theme_option_desc" class="dimc desc" style="font-size: 0.9em; padding-left: 5px; padding-bottom: 6px;">
|
||||
Choose your system theme for current WebUI
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- INFO: Change language -->
|
||||
<div id="lang_page_toggle" class="small_card dimc card_animation" style="margin-top: 15px; justify-content: space-between; display: flex; align-items: center;">
|
||||
<div class="dimc" style="display: inline-block;">
|
||||
<div id="sys_lang_option_title" class="dimc content" style="font-size: 1.1em; padding-left: 5px; padding-top: 6px; padding-bottom: 2px;">
|
||||
Change language
|
||||
</div>
|
||||
<div id="sys_lang_option_desc" class="dimc desc" style="font-size: 0.9em; padding-left: 5px; padding-bottom: 6px;">
|
||||
Change to your new language
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- INFO: Error history -->
|
||||
<div id="errorh_page_toggle" class="small_card dimc card_animation" style="margin-top: 15px; justify-content: space-between; display: flex; align-items: center;">
|
||||
<div class="dimc" style="display: inline-block;">
|
||||
<div id="sys_errorh_title" class="dimc content" style="font-size: 1.1em; padding-left: 5px; padding-top: 6px; padding-bottom: 2px;">
|
||||
Error History
|
||||
</div>
|
||||
<div id="sys_errorh_desc" class="dimc desc" style="font-size: 0.9em; padding-left: 5px; padding-bottom: 6px;">
|
||||
View all of your error log
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- INFO: Credit card -->
|
||||
<div class="dim card" style="margin-top: 15px;">
|
||||
<div class="dimc content">
|
||||
<div id="mcre_title" class="dimc" style="font-size: 1.1em">Module Developer</div>
|
||||
<div credit-link="github.com/PerformanC" class="dimc desc" style="font-size: 0.9em; margin-top: 3px; cursor: pointer;">
|
||||
The PerformanC Organization
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dimc content" style="margin-top: 6px;">
|
||||
<div id="omcre_title" class="dimc" style="font-size: 1.1em">Original Module Developer</div>
|
||||
<div class="dimc" style="font-size: 0.9em; margin-top: 3px; align-items: center; display: flex;">
|
||||
<div credit-link="github.com/Dr-TSNG" class="dimc desc" style="cursor: pointer;">
|
||||
Nullptr
|
||||
</div>
|
||||
<div class="dimc desc">,</div>
|
||||
<div credit-link="github.com/5ec1cff" class="dimc desc" style="margin-left: 2px; cursor: pointer;">
|
||||
5ec1cff
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dimc" style="margin-top: 6px;">
|
||||
<div id="webcre_title" class="dimc" style="font-size: 1.1em">WebUI Developer</div>
|
||||
<div credit-link="github.com/RainyXeon" class="dimc desc" style="font-size: 0.9em; margin-top: 3px; cursor: pointer;">
|
||||
RainyXeon
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- INFO: License card -->
|
||||
<div class="dim card" style="margin-top: 15px;">
|
||||
<div class="dimc content">
|
||||
<div id="mlic_title" class="dimc" style="font-size: 1.1em">Module License</div>
|
||||
<div class="dimc desc" style="font-size: 0.9em; margin-top: 3px;">GPL (Nullptr), BSD 2-Clause</div>
|
||||
</div>
|
||||
|
||||
<div class="dimc" style="margin-top: 6px;">
|
||||
<div id="mweb_title" class="dimc" style="font-size: 1.1em">WebUI License</div>
|
||||
<div class="dimc desc" style="font-size: 0.9em; margin-top: 3px;">BSD 2-Clause</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- INFO: System theme small page -->
|
||||
<div id="small_panel_theme" class="panel">
|
||||
<div class="header" style="padding-left: 20px; display: flex; align-items: center; justify-content: initial;">
|
||||
<div id="sp_theme_close" style="width: 36px; height: 36px; margin-right: 6px;">
|
||||
<img src="./assets/back.svg"/>
|
||||
</div>
|
||||
<div id="small_panel_theme_title">Theme</div>
|
||||
</div>
|
||||
<div style="padding: 47px 0px;"></div>
|
||||
<div style="width: 100%; height: fit-content;">
|
||||
<div theme-data="amoled" class="dim card card_animation" style="padding: 25px 15px; cursor: pointer;">
|
||||
<div theme-data="amoled" id="small_panel_theme_amoled_option" class="dimc" style="font-size: 1.1em;">Amoled</div>
|
||||
</div>
|
||||
<div theme-data="dark" class="dim card card_animation" style="padding: 25px 15px; cursor: pointer;">
|
||||
<div theme-data="dark" id="small_panel_theme_dark_option" class="dimc" style="font-size: 1.1em;">Dark</div>
|
||||
</div>
|
||||
<div theme-data="light" class="dim card card_animation" style="padding: 25px 15px; cursor: pointer;">
|
||||
<div theme-data="light" id="small_panel_theme_light_option" class="dimc" style="font-size: 1.1em;">Light</div>
|
||||
</div>
|
||||
<div theme-data="monochrome" class="dim card card_animation" style="padding: 25px 15px; cursor: pointer;">
|
||||
<div theme-data="monochrome" id="small_panel_theme_monochrome_option" class="dimc" style="font-size: 1.1em;">Monochrome</div>
|
||||
</div>
|
||||
<div theme-data="system" class="dim card card_animation" style="padding: 25px 15px; cursor: pointer;">
|
||||
<div theme-data="system" id="small_panel_theme_system_option" class="dimc" style="font-size: 1.1em;">System Based</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- INFO: Language small page -->
|
||||
<div id="small_panel_language" class="panel">
|
||||
<div class="header" style="padding-left: 20px; display: flex; align-items: center; justify-content: initial;">
|
||||
<div id="sp_lang_close" style="width: 36px; height: 36px; margin-right: 6px;">
|
||||
<img src="./assets/back.svg"/>
|
||||
</div>
|
||||
<div id="small_panel_lang_title">Choose your new language</div>
|
||||
</div>
|
||||
<div style="padding: 47px 0px;"></div>
|
||||
<div id="lang_modal_list" style="width: 100%; height: fit-content;">
|
||||
<!-- N/A -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- INFO: Error small page -->
|
||||
<div id="small_panel_errorh" class="panel">
|
||||
<div id="errorh_small_panel_header" class="header" style="padding-left: 20px; display: flex; align-items: center; justify-content: initial;">
|
||||
<div id="sp_errorh_close" style="width: 36px; height: 36px; margin-right: 6px;">
|
||||
<img src="./assets/back.svg"/>
|
||||
</div>
|
||||
<div id="small_panel_errorh_title">Error History</div>
|
||||
</div>
|
||||
<div style="display: block; height: 100%;">
|
||||
<div class="errorh_button_container">
|
||||
<div id="errorh_copy" class="errorh_button">COPY</div>
|
||||
<div id="errorh_clear_all" class="errorh_button">CLEAR ALL LOG</div>
|
||||
</div>
|
||||
<textarea id="errorh_panel" class="errorh_textarea" placeholder="No error log recorded here!" disabled></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- INFO: Bottom navbar -->
|
||||
<div id="navbar_support_div" style="display: none;">
|
||||
<div id="navbar" class="navbar radios">
|
||||
<div class="navicon_support nav_dimc">
|
||||
<input id="n_home" type="radio" name="navbutton" value="home" checked/>
|
||||
<label class="navicon_support radio nav_dimc" for="n_home">
|
||||
|
||||
<div class="navicon_support nav_dimc" style="display: grid; place-items: center;">
|
||||
<div class="navicon_support nav_dimc" style="display: grid; place-items: center; width: 65px;">
|
||||
<div style="grid-area: 1 / 1;" id="ni_home"></div>
|
||||
<div style="grid-area: 1 / 1;" id="nibg_home" class="navicon"></div>
|
||||
</div>
|
||||
<div id="nav_home_title" class="navtitle nav_dimc">Home</div>
|
||||
</div>
|
||||
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="nav_dimc">
|
||||
<input id="n_modules" type="radio" name="navbutton" value="modules"/>
|
||||
<label class="radio nav_dimc" for="n_modules">
|
||||
|
||||
<div class="nav_dimc" style="display: grid; place-items: center;">
|
||||
<div class="nav_dimc" style="display: grid; place-items: center; width: 65px;">
|
||||
<div style="grid-area: 1 / 1;" id="ni_modules"></div>
|
||||
<div style="grid-area: 1 / 1;" id="nibg_modules" class="navicon"></div>
|
||||
</div>
|
||||
<div id="nav_modules_title" class="navtitle nav_dimc">Modules</div>
|
||||
</div>
|
||||
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="nav_dimc">
|
||||
<input id="n_actions" type="radio" name="navbutton" value="actions"/>
|
||||
<label class="radio nav_dimc" for="n_actions">
|
||||
|
||||
<div class="nav_dimc" style="display: grid; place-items: center;">
|
||||
<div class="nav_dimc" style="display: grid; place-items: center; width: 65px;">
|
||||
<div style="grid-area: 1 / 1;" id="ni_actions"></div>
|
||||
<div style="grid-area: 1 / 1;" id="nibg_actions" class="navicon"></div>
|
||||
</div>
|
||||
<div id="nav_actions_title" class="navtitle nav_dimc">Actions</div>
|
||||
</div>
|
||||
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="navicon_support nav_dimc">
|
||||
<input id="n_settings" type="radio" name="navbutton" value="settings"/>
|
||||
<label class="navicon_support radio nav_dimc" for="n_settings">
|
||||
|
||||
<div class="navicon_support nav_dimc" style="display: grid; place-items: center;">
|
||||
<div class="navicon_support nav_dimc" style="display: grid; place-items: center; width: 65px;">
|
||||
<div style="grid-area: 1 / 1;" id="ni_settings"></div>
|
||||
<div style="grid-area: 1 / 1;" id="nibg_settings" class="navicon"></div>
|
||||
</div>
|
||||
<div id="nav_settings_title" class="navtitle nav_dimc">Settings</div>
|
||||
</div>
|
||||
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
11
webroot/js/browserRedirect.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import { exec } from './kernelsu.js'
|
||||
|
||||
document.addEventListener('click', async (event) => {
|
||||
const getLink = event.target.getAttribute('credit-link')
|
||||
if (!getLink || typeof getLink !== 'string') return;
|
||||
|
||||
const ptrace64Cmd = await exec(`am start -a android.intent.action.VIEW -d https://${getLink}`).catch(() => {
|
||||
return window.open(`https://${getLink}`, "_blank", 'toolbar=0,location=0,menubar=0')
|
||||
})
|
||||
if (ptrace64Cmd.errno !== 0) return window.open(`https://${getLink}`, "_blank", 'toolbar=0,location=0,menubar=0')
|
||||
}, false)
|
||||
16
webroot/js/errorCatcher.js
Normal file
@@ -0,0 +1,16 @@
|
||||
function setError(place, issue) {
|
||||
const fullErrorLog = setErrorData(`${place}: ${issue}`)
|
||||
document.getElementById('errorh_panel').innerHTML = fullErrorLog
|
||||
}
|
||||
|
||||
function setErrorData(errorLog) {
|
||||
const getPrevious = localStorage.getItem('/system/error')
|
||||
const finalLog = getPrevious && getPrevious.length !== 0 ? getPrevious + `\n` + errorLog : errorLog
|
||||
|
||||
localStorage.setItem('/system/error', finalLog)
|
||||
return finalLog
|
||||
}
|
||||
|
||||
if (window.onerror) window.onerror = (err) => {
|
||||
setError('WebUI', err.stack ? err.stack : err.message)
|
||||
}
|
||||
6
webroot/js/errorScreen.js
Normal file
@@ -0,0 +1,6 @@
|
||||
const button = document.getElementById('esc-copy-button')
|
||||
const log = document.getElementById('esc-log')
|
||||
|
||||
button.addEventListener('click', () => {
|
||||
navigator.clipboard.writeText(log.innerHTML)
|
||||
})
|
||||
117
webroot/js/kernelsu.js
Normal file
@@ -0,0 +1,117 @@
|
||||
/* https://github.com/tiann/KernelSU/tree/main/js / https://www.npmjs.com/package/kernelsu */
|
||||
|
||||
let callbackCounter = 0;
|
||||
function getUniqueCallbackName(prefix) {
|
||||
return `${prefix}_callback_${Date.now()}_${callbackCounter++}`;
|
||||
}
|
||||
|
||||
export function exec(command, options) {
|
||||
if (typeof options === "undefined") {
|
||||
options = {};
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
// Generate a unique callback function name
|
||||
const callbackFuncName = getUniqueCallbackName("exec");
|
||||
|
||||
// Define the success callback function
|
||||
window[callbackFuncName] = (errno, stdout, stderr) => {
|
||||
resolve({ errno, stdout, stderr });
|
||||
cleanup(callbackFuncName);
|
||||
};
|
||||
|
||||
function cleanup(successName) {
|
||||
delete window[successName];
|
||||
}
|
||||
|
||||
try {
|
||||
ksu.exec(command, JSON.stringify(options), callbackFuncName);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
cleanup(callbackFuncName);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function Stdio() {
|
||||
this.listeners = {};
|
||||
}
|
||||
|
||||
Stdio.prototype.on = function (event, listener) {
|
||||
if (!this.listeners[event]) {
|
||||
this.listeners[event] = [];
|
||||
}
|
||||
this.listeners[event].push(listener);
|
||||
};
|
||||
|
||||
Stdio.prototype.emit = function (event, ...args) {
|
||||
if (this.listeners[event]) {
|
||||
this.listeners[event].forEach((listener) => listener(...args));
|
||||
}
|
||||
};
|
||||
|
||||
function ChildProcess() {
|
||||
this.listeners = {};
|
||||
this.stdin = new Stdio();
|
||||
this.stdout = new Stdio();
|
||||
this.stderr = new Stdio();
|
||||
}
|
||||
|
||||
ChildProcess.prototype.on = function (event, listener) {
|
||||
if (!this.listeners[event]) {
|
||||
this.listeners[event] = [];
|
||||
}
|
||||
this.listeners[event].push(listener);
|
||||
};
|
||||
|
||||
ChildProcess.prototype.emit = function (event, ...args) {
|
||||
if (this.listeners[event]) {
|
||||
this.listeners[event].forEach((listener) => listener(...args));
|
||||
}
|
||||
};
|
||||
|
||||
export function spawn(command, args, options) {
|
||||
if (typeof args === "undefined") {
|
||||
args = [];
|
||||
} else if (!(args instanceof Array)) {
|
||||
// allow for (command, options) signature
|
||||
options = args;
|
||||
}
|
||||
|
||||
if (typeof options === "undefined") {
|
||||
options = {};
|
||||
}
|
||||
|
||||
const child = new ChildProcess();
|
||||
const childCallbackName = getUniqueCallbackName("spawn");
|
||||
window[childCallbackName] = child;
|
||||
|
||||
function cleanup(name) {
|
||||
delete window[name];
|
||||
}
|
||||
|
||||
child.on("exit", code => {
|
||||
cleanup(childCallbackName);
|
||||
});
|
||||
|
||||
try {
|
||||
ksu.spawn(
|
||||
command,
|
||||
JSON.stringify(args),
|
||||
JSON.stringify(options),
|
||||
childCallbackName
|
||||
);
|
||||
} catch (error) {
|
||||
child.emit("error", error);
|
||||
cleanup(childCallbackName);
|
||||
}
|
||||
return child;
|
||||
}
|
||||
|
||||
export function fullScreen(isFullScreen) {
|
||||
ksu.fullScreen(isFullScreen);
|
||||
}
|
||||
|
||||
export function toast(message) {
|
||||
ksu.toast(message);
|
||||
}
|
||||
63
webroot/js/language.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import { exec } from './kernelsu.js'
|
||||
|
||||
import { setError } from './main.js'
|
||||
import { translateActionsPage } from './translate/actions.js'
|
||||
import { translateHomePage } from './translate/home.js'
|
||||
import { translateModulesPage } from './translate/modules.js'
|
||||
import { translateSettingsPage } from './translate/settings.js'
|
||||
|
||||
export async function setNewLanguage(locate, initialize) {
|
||||
const main_html = document.getElementById('main_html')
|
||||
const old_translations = await getTranslations(initialize ? 'en_US' : localStorage.getItem('/system/language'))
|
||||
const new_translations = await getTranslations(locate)
|
||||
|
||||
if (locate.includes('ar_')) main_html.setAttribute("dir", "rtl");
|
||||
else main_html.setAttribute("dir", "none");
|
||||
|
||||
translateHomePage(old_translations, new_translations)
|
||||
translateModulesPage(new_translations)
|
||||
translateActionsPage(old_translations, new_translations)
|
||||
translateSettingsPage(new_translations)
|
||||
|
||||
/* INFO: navbar info */
|
||||
document.getElementById('nav_home_title').innerHTML = new_translations.page.home.header
|
||||
document.getElementById('nav_modules_title').innerHTML = new_translations.page.modules.header
|
||||
document.getElementById('nav_actions_title').innerHTML = new_translations.page.actions.header
|
||||
document.getElementById('nav_settings_title').innerHTML = new_translations.page.settings.header
|
||||
|
||||
/* INFO: Language small page */
|
||||
document.getElementById('small_panel_lang_title').innerHTML = new_translations.smallPage.language.header
|
||||
|
||||
/* INFO: Theme small page */
|
||||
document.getElementById('small_panel_theme_title').innerHTML = new_translations.smallPage.theme.header
|
||||
document.getElementById('small_panel_theme_dark_option').innerHTML = new_translations.smallPage.theme.dark
|
||||
document.getElementById('small_panel_theme_light_option').innerHTML = new_translations.smallPage.theme.light
|
||||
document.getElementById('small_panel_theme_system_option').innerHTML = new_translations.smallPage.theme.system
|
||||
|
||||
/* INFO: Error history small page */
|
||||
document.getElementById('errorh_copy').innerHTML = new_translations.smallPage.errorh.buttons.copy
|
||||
document.getElementById('errorh_clear_all').innerHTML = new_translations.smallPage.errorh.buttons.clear
|
||||
document.getElementById('small_panel_errorh_title').innerHTML = new_translations.smallPage.errorh.header
|
||||
document.getElementById('errorh_panel').placeholder = new_translations.smallPage.errorh.placeholder
|
||||
}
|
||||
|
||||
export async function getTranslations(locate) {
|
||||
const translateData = await fetch(`./lang/${locate}.json`)
|
||||
.catch((err) => setError('WebUI', err.stack ? err.stack : err.message))
|
||||
|
||||
return translateData.json()
|
||||
}
|
||||
|
||||
export async function getAvailableLanguages() {
|
||||
const lsCmd = await exec('ls /data/adb/modules/rezygisk/webroot/lang')
|
||||
|
||||
if (lsCmd.errno !== 0) return setError('WebUI', lsCmd.stderr)
|
||||
|
||||
const languages = []
|
||||
lsCmd.stdout.split('\n').forEach((lang) => {
|
||||
if (lang.length !== 0)
|
||||
languages.push(lang.replace('.json', ''))
|
||||
})
|
||||
|
||||
return languages
|
||||
}
|
||||
262
webroot/js/main.js
Normal file
@@ -0,0 +1,262 @@
|
||||
import { fullScreen, exec, toast } from './kernelsu.js'
|
||||
|
||||
import { setNewLanguage, getTranslations } from './language.js'
|
||||
|
||||
export function setError(place, issue) {
|
||||
const fullErrorLog = setErrorData(`${place}: ${issue}`)
|
||||
document.getElementById('errorh_panel').innerHTML = fullErrorLog
|
||||
|
||||
toast(`${place}: ${issue}`)
|
||||
}
|
||||
|
||||
export function setLangData(mode) {
|
||||
localStorage.setItem('/system/language', mode)
|
||||
|
||||
return localStorage.getItem('/system/language')
|
||||
}
|
||||
|
||||
export function setErrorData(errorLog) {
|
||||
const getPrevious = localStorage.getItem('/system/error')
|
||||
const finalLog = getPrevious && getPrevious.length !== 0 ? getPrevious + `\n` + errorLog : errorLog
|
||||
|
||||
localStorage.setItem('/system/error', finalLog)
|
||||
|
||||
return finalLog
|
||||
}
|
||||
|
||||
async function getModuleNames(modules) {
|
||||
const fullCommand = modules.map((mod) => {
|
||||
let propPath = `/data/adb/modules/${mod.id}/module.prop`
|
||||
|
||||
return `printf % ; if test -f "${propPath}"; then /system/bin/grep '^name=' "${propPath}" | /system/bin/cut -d '=' -f 2- 2>/dev/null || true; else true; fi ; printf "\\n"`
|
||||
}).join(' ; ')
|
||||
|
||||
const result = await exec(fullCommand)
|
||||
if (result.errno !== 0) {
|
||||
setError('getModuleNames', 'Failed to execute command to retrieve module list names')
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
return result.stdout.split('\n\n')
|
||||
}
|
||||
|
||||
(async () => {
|
||||
/* INFO: Test ksu module availability */
|
||||
exec('echo "Hello world!"')
|
||||
.then(() => console.log('[kernelsu.js] Package is ready!'))
|
||||
.catch(err => {
|
||||
console.log('[kernelsu.js] Package is not ready! Below is error:')
|
||||
console.error(err)
|
||||
})
|
||||
|
||||
fullScreen(true)
|
||||
|
||||
let sys_lang = localStorage.getItem('/system/language')
|
||||
|
||||
if (!sys_lang) sys_lang = setLangData('en_US')
|
||||
if (sys_lang !== 'en_US') await setNewLanguage(sys_lang, true)
|
||||
|
||||
const translations = await getTranslations(sys_lang)
|
||||
|
||||
const loading_screen = document.getElementById('loading_screen')
|
||||
const bottom_nav = document.getElementById('navbar_support_div')
|
||||
|
||||
const rootCss = document.querySelector(':root')
|
||||
|
||||
const rezygisk_state = document.getElementById('rezygisk_state')
|
||||
const rezygisk_icon_state = document.getElementById('rezygisk_icon_state')
|
||||
|
||||
const version = document.getElementById('version')
|
||||
const root_impl = document.getElementById('root_impl')
|
||||
|
||||
const monitor_status = document.getElementById('monitor_status')
|
||||
|
||||
const zygote_divs = [
|
||||
document.getElementById('zygote64'),
|
||||
document.getElementById('zygote32')
|
||||
]
|
||||
|
||||
const zygote_status_divs = [
|
||||
document.getElementById('zygote64_status'),
|
||||
document.getElementById('zygote32_status')
|
||||
]
|
||||
|
||||
const androidVersionCmd = await exec('/system/bin/getprop ro.build.version.release')
|
||||
if (androidVersionCmd.errno !== 0) return setError('WebUI', androidVersionCmd.stderr)
|
||||
|
||||
document.getElementById('android_version_div').innerHTML = androidVersionCmd.stdout
|
||||
console.log('[rezygisk.js] Android version: ', androidVersionCmd.stdout)
|
||||
|
||||
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.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}`)
|
||||
|
||||
if (catCmd.errno !== 0) {
|
||||
console.error('[rezygisk.js] Failed to retrieve ReZygisk module information:', catCmd.stderr)
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* INFO: Just ensure that they won't appear unless there's info */
|
||||
zygote_divs.forEach((zygote_div) => {
|
||||
zygote_div.style.display = 'none'
|
||||
})
|
||||
|
||||
version.innerHTML = catCmd.stdout.split('\n').find((line) => line.startsWith('version=')).substring('version='.length).trim()
|
||||
|
||||
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)
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const ReZygiskState = JSON.parse(stateCmd.stdout)
|
||||
|
||||
root_impl.innerHTML = ReZygiskState.root
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const expectedWorking = (ReZygiskState.zygote['64'] !== undefined ? 1 : 0) + (ReZygiskState.zygote['32'] !== undefined ? 1 : 0)
|
||||
let actuallyWorking = 0
|
||||
|
||||
if (ReZygiskState.zygote['64'] !== undefined) {
|
||||
const zygote64 = ReZygiskState.zygote['64']
|
||||
|
||||
zygote_divs[0].style.display = 'block'
|
||||
|
||||
switch (zygote64) {
|
||||
case 1: {
|
||||
zygote_status_divs[0].innerHTML = translations.page.home.info.zygote.injected
|
||||
|
||||
actuallyWorking++
|
||||
|
||||
break
|
||||
}
|
||||
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) {
|
||||
rezygisk_state.innerHTML = translations.page.home.status.notWorking
|
||||
} else if (expectedWorking === actuallyWorking) {
|
||||
rezygisk_state.innerHTML = translations.page.home.status.ok
|
||||
|
||||
rootCss.style.setProperty('--bright', '#3a4857')
|
||||
rezygisk_icon_state.innerHTML = '<img class="brightc" src="assets/tick.svg">'
|
||||
} else {
|
||||
rezygisk_state.innerHTML = translations.page.home.status.partially
|
||||
|
||||
rootCss.style.setProperty('--bright', '#766000')
|
||||
rezygisk_icon_state.innerHTML = '<img class="brightc" src="assets/warn.svg">'
|
||||
}
|
||||
|
||||
const all_modules = []
|
||||
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_bit)
|
||||
} else {
|
||||
all_modules.push({
|
||||
id: module_id,
|
||||
name: null,
|
||||
bitsUsed: [ daemon_bit ]
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
if (all_modules.length !== 0) {
|
||||
document.getElementById('modules_list_not_avaliable').style.display = 'none'
|
||||
|
||||
const module_names = await getModuleNames(all_modules)
|
||||
module_names.forEach((module_name, i) => all_modules[i].name = module_name)
|
||||
|
||||
console.log(`[rezygisk.js] Module list:`)
|
||||
console.log(all_modules)
|
||||
|
||||
const modules_list = document.getElementById('modules_list')
|
||||
|
||||
all_modules.forEach((module) => {
|
||||
modules_list.innerHTML +=
|
||||
`<div class="dim card" style="padding: 25px 15px; cursor: pointer;">
|
||||
<div class="dimc" style="font-size: 1.1em;">${module.name}</div>
|
||||
<div class="dimc desc" style="font-size: 0.9em; margin-top: 3px; white-space: nowrap; align-items: center; display: flex;">
|
||||
<div class="dimc arch_desc">${translations.page.modules.arch}</div>
|
||||
<div class="dimc" style="margin-left: 5px;">${module.bitsUsed.join(' / ')}</div>
|
||||
</div>
|
||||
</div>`
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
/* INFO: This hides the throbber screen */
|
||||
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')
|
||||
})().catch((err) => setError('WebUI', err.stack ? err.stack : err.message))
|
||||
38
webroot/js/monitorActions.js
Normal file
@@ -0,0 +1,38 @@
|
||||
import { exec, toast } from './kernelsu.js'
|
||||
|
||||
import { getTranslations } from './language.js'
|
||||
|
||||
const monitor_start = document.getElementById('monitor_start_button')
|
||||
const monitor_stop = document.getElementById('monitor_stop_button')
|
||||
const monitor_pause = document.getElementById('monitor_pause_button')
|
||||
|
||||
const monitor_status = document.getElementById('monitor_status');
|
||||
|
||||
(async () => {
|
||||
const sys_lang = localStorage.getItem('/system/language')
|
||||
const translations = await getTranslations(sys_lang || 'en_US')
|
||||
|
||||
if (monitor_start) {
|
||||
monitor_start.addEventListener('click', () => {
|
||||
if (![ translations.page.actions.status.tracing, translations.page.actions.status.stopping, translations.page.actions.status.stopped ].includes(monitor_status.innerHTML)) return;
|
||||
|
||||
monitor_status.innerHTML = translations.page.actions.status.tracing
|
||||
|
||||
exec('/data/adb/modules/rezygisk/bin/zygisk-ptrace64 ctl start')
|
||||
})
|
||||
|
||||
monitor_stop.addEventListener('click', () => {
|
||||
monitor_status.innerHTML = translations.page.actions.status.exiting
|
||||
|
||||
exec('/data/adb/modules/rezygisk/bin/zygisk-ptrace64 ctl exit')
|
||||
})
|
||||
|
||||
monitor_pause.addEventListener('click', () => {
|
||||
if (![ translations.page.actions.status.tracing, translations.page.actions.status.stopping, translations.page.actions.status.stopped ].includes(monitor_status.innerHTML)) return;
|
||||
|
||||
monitor_status.innerHTML = translations.page.actions.status.stopped
|
||||
|
||||
exec('/data/adb/modules/rezygisk/bin/zygisk-ptrace64 ctl stop')
|
||||
})
|
||||
}
|
||||
})()
|
||||
52
webroot/js/navbar.js
Normal file
@@ -0,0 +1,52 @@
|
||||
const navbar_data_tag = document.getElementById('cache-navbar-previous')
|
||||
const small_panel_data_tag = document.getElementById('cache-page-small-previous')
|
||||
|
||||
setData('home', navbar_data_tag)
|
||||
|
||||
document.getElementById('panel_home').classList.toggle('show')
|
||||
document.getElementById(`nibg_home`).classList.toggle('show')
|
||||
|
||||
document.querySelectorAll('[name=navbutton]').forEach((element) => {
|
||||
element.addEventListener('click', (event) => {
|
||||
let smallPagePass = false
|
||||
|
||||
const value = event.target.value
|
||||
const previous = !navbar_data_tag.getAttribute('content')
|
||||
? setData('home', navbar_data_tag)
|
||||
: navbar_data_tag.getAttribute('content')
|
||||
|
||||
const small_panel = small_panel_data_tag.getAttribute('content')
|
||||
|
||||
if (small_panel && small_panel.length !== 0) {
|
||||
document.getElementById(`small_panel_${small_panel}`).classList.remove('show')
|
||||
small_panel_data_tag.removeAttribute('content')
|
||||
smallPagePass = true
|
||||
}
|
||||
|
||||
if (previous === value && !smallPagePass) return;
|
||||
|
||||
/* INFO: Disable icon on old state */
|
||||
const pre_input = document.getElementById(`n_${previous}`)
|
||||
const pre_background = document.getElementById(`nibg_${previous}`)
|
||||
|
||||
document.getElementById(`panel_${previous}`).classList.remove('show')
|
||||
pre_input.removeAttribute('checked')
|
||||
pre_background.classList.remove('show')
|
||||
|
||||
/* INFO: Enable icon on new state */
|
||||
const curr_input = document.getElementById(`n_${value}`)
|
||||
const i_background = document.getElementById(`nibg_${value}`)
|
||||
|
||||
document.getElementById(`panel_${value}`).classList.toggle('show')
|
||||
curr_input.setAttribute('checked', '')
|
||||
i_background.classList.toggle('show')
|
||||
|
||||
setData(value, navbar_data_tag)
|
||||
})
|
||||
})
|
||||
|
||||
function setData(data, tag) {
|
||||
tag.setAttribute('content', data)
|
||||
|
||||
return data
|
||||
}
|
||||
10
webroot/js/restoreError.js
Normal file
@@ -0,0 +1,10 @@
|
||||
const errorh_panel = document.getElementById('errorh_panel')
|
||||
let sys_error = localStorage.getItem('/system/error')
|
||||
|
||||
if (!sys_error) {
|
||||
localStorage.setItem('/system/error', '')
|
||||
|
||||
sys_error = localStorage.getItem('/system/error')
|
||||
}
|
||||
|
||||
if (sys_error.length !== 0) errorh_panel.innerHTML = sys_error
|
||||
47
webroot/js/smallPage/errorHistory.js
Normal file
@@ -0,0 +1,47 @@
|
||||
import { smallPageDisabler } from '../smallPageDesabler.js'
|
||||
const panel = document.getElementById('errorh_panel')
|
||||
|
||||
/* INFO: Event setup */
|
||||
const navbar_data_tag = document.getElementById('cache-navbar-previous')
|
||||
const small_panel_data_tag = document.getElementById('cache-page-small-previous')
|
||||
const fallback_open = document.getElementById('cache-fallback-open')
|
||||
|
||||
document.getElementById('errorh_page_toggle').addEventListener('click', () => {
|
||||
const previous = !navbar_data_tag.getAttribute('content') ? setData('home', small_panel_data_tag) : navbar_data_tag.getAttribute('content')
|
||||
document.getElementById(`panel_${previous}`).classList.remove('show')
|
||||
document.getElementById('small_panel_errorh').classList.toggle('show')
|
||||
small_panel_data_tag.setAttribute('content', 'errorh')
|
||||
})
|
||||
|
||||
document.getElementById('backport_errorh').addEventListener('click', () => {
|
||||
const previous = !navbar_data_tag.getAttribute('content') ? setData('home', small_panel_data_tag) : navbar_data_tag.getAttribute('content')
|
||||
document.getElementById(`panel_${previous}`).classList.remove('show')
|
||||
document.getElementById('loading_screen').style.display = 'none'
|
||||
document.getElementById('small_panel_errorh').classList.toggle('show')
|
||||
document.getElementById('errorh_panel').style.bottom = '1em'
|
||||
fallback_open.setAttribute('content', 'opened')
|
||||
small_panel_data_tag.setAttribute('content', 'errorh')
|
||||
})
|
||||
|
||||
document.getElementById('sp_errorh_close').addEventListener('click', () => {
|
||||
const is_fallback = fallback_open.getAttribute('content')
|
||||
if (is_fallback) {
|
||||
document.getElementById('errorh_panel').style.bottom = '1em'
|
||||
document.getElementById('loading_screen').style.display = 'flex'
|
||||
}
|
||||
smallPageDisabler('errorh', is_fallback ? 'home' : 'settings', is_fallback ? 'home' : null)
|
||||
})
|
||||
document.getElementById('errorh_copy').addEventListener('click', () => {
|
||||
navigator.clipboard.writeText(panel.innerHTML)
|
||||
})
|
||||
|
||||
document.getElementById('errorh_clear_all').addEventListener('click', () => {
|
||||
panel.innerHTML = ''
|
||||
localStorage.setItem('/system/error', '')
|
||||
})
|
||||
|
||||
function setData(mode, tag) {
|
||||
tag.setAttribute('content', mode)
|
||||
|
||||
return mode
|
||||
}
|
||||
54
webroot/js/smallPage/language.js
Normal file
@@ -0,0 +1,54 @@
|
||||
import {
|
||||
getTranslations,
|
||||
setNewLanguage,
|
||||
getAvailableLanguages
|
||||
} from '../language.js'
|
||||
import { smallPageDisabler } from '../smallPageDesabler.js'
|
||||
|
||||
/* INFO: Initial setup */
|
||||
let index = 0
|
||||
|
||||
async function setAvailableLanguage() {
|
||||
const availableLanguages = await getAvailableLanguages()
|
||||
|
||||
for (index; index < availableLanguages.length; index++) {
|
||||
const langCode = availableLanguages[index]
|
||||
const langData = await getTranslations(langCode)
|
||||
|
||||
document.getElementById('lang_modal_list').innerHTML += `
|
||||
<div lang-data="${langCode}" class="dim card card_animation" style="padding: 25px 15px; cursor: pointer;">
|
||||
<div lang-data="${langCode}" class="dimc" style="font-size: 1.1em;">${langData.langName}</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
}
|
||||
setAvailableLanguage()
|
||||
|
||||
/* INFO: Event setup */
|
||||
const navbar_data_tag = document.getElementById('cache-navbar-previous')
|
||||
const small_panel_data_tag = document.getElementById('cache-page-small-previous')
|
||||
|
||||
document.getElementById('lang_page_toggle').addEventListener('click', () => {
|
||||
const previous = !navbar_data_tag.getAttribute('content') ? setData('home', small_panel_data_tag) : navbar_data_tag.getAttribute('content')
|
||||
document.getElementById(`panel_${previous}`).classList.remove('show')
|
||||
document.getElementById('small_panel_language').classList.toggle('show')
|
||||
small_panel_data_tag.setAttribute('content', 'language')
|
||||
})
|
||||
|
||||
document.getElementById('sp_lang_close').addEventListener('click', () => smallPageDisabler('language', 'settings'))
|
||||
|
||||
document.addEventListener('click', async (event) => {
|
||||
const getLangLocate = event.target.getAttribute('lang-data')
|
||||
if (!getLangLocate || typeof getLangLocate !== 'string') return
|
||||
|
||||
smallPageDisabler('language', 'settings')
|
||||
await setNewLanguage(getLangLocate)
|
||||
|
||||
localStorage.setItem('/system/language', getLangLocate)
|
||||
}, false)
|
||||
|
||||
function setData(data, tag) {
|
||||
tag.setAttribute('content', data)
|
||||
|
||||
return data
|
||||
}
|
||||
75
webroot/js/smallPage/theme.js
Normal file
@@ -0,0 +1,75 @@
|
||||
import { smallPageDisabler } from '../smallPageDesabler.js'
|
||||
import { setAmoled } from '../themes/amoled.js'
|
||||
import { setDark } from '../themes/dark.js'
|
||||
import { setLight } from '../themes/light.js'
|
||||
import { setMonochrome } from '../themes/monochrome.js'
|
||||
|
||||
// INFO: requirement variables
|
||||
let sys_theme
|
||||
const page_toggle = document.getElementById('theme_page_toggle')
|
||||
const themeList = {
|
||||
amoled: () => setAmoled(true),
|
||||
dark: () => setDark(true),
|
||||
light: () => setLight(true),
|
||||
monochrome: () => setMonochrome(true),
|
||||
system: (unavaliable) => {
|
||||
const isDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
if (isDark && unavaliable) setDark()
|
||||
else setLight()
|
||||
},
|
||||
}
|
||||
const setData = (mode) => {
|
||||
localStorage.setItem('/system/theme', mode)
|
||||
return mode
|
||||
}
|
||||
|
||||
// INFO: Initial open logic
|
||||
sys_theme = localStorage.getItem('/system/theme')
|
||||
if (!sys_theme) sys_theme = setData('dark')
|
||||
themeList[sys_theme](true)
|
||||
|
||||
// INFO: Event logic
|
||||
const navbar_data_tag = document.getElementById('cache-navbar-previous')
|
||||
const small_panel_data_tag = document.getElementById('cache-page-small-previous')
|
||||
|
||||
document.getElementById('sp_theme_close').addEventListener('click', () => smallPageDisabler('theme', 'settings'))
|
||||
|
||||
document.addEventListener('click', async (event) => {
|
||||
const themeListKey = Object.keys(themeList)
|
||||
const getThemeMode = event.target.getAttribute('theme-data')
|
||||
|
||||
if (!getThemeMode || typeof getThemeMode !== 'string' || !themeListKey.includes(getThemeMode)) return
|
||||
|
||||
themeList[getThemeMode](true)
|
||||
|
||||
smallPageDisabler('theme', 'settings')
|
||||
|
||||
sys_theme = setData(getThemeMode)
|
||||
}, false)
|
||||
|
||||
page_toggle.addEventListener('click', () => {
|
||||
const previous = !navbar_data_tag.getAttribute('content') ? setTagData('home', small_panel_data_tag) : navbar_data_tag.getAttribute('content')
|
||||
document.getElementById(`panel_${previous}`).classList.remove('show')
|
||||
document.getElementById('small_panel_theme').classList.toggle('show')
|
||||
small_panel_data_tag.setAttribute('content', 'theme')
|
||||
})
|
||||
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
|
||||
if (sys_theme !== "system") return
|
||||
const newColorScheme = event.matches ? "dark" : "light";
|
||||
|
||||
switch (newColorScheme) {
|
||||
case 'dark':
|
||||
setDark()
|
||||
break
|
||||
case 'light':
|
||||
setLight()
|
||||
break
|
||||
}
|
||||
});
|
||||
|
||||
function setTagData(data, tag) {
|
||||
tag.setAttribute('content', data)
|
||||
|
||||
return data
|
||||
}
|
||||
26
webroot/js/smallPageDesabler.js
Normal file
@@ -0,0 +1,26 @@
|
||||
export function smallPageDisabler(page_name, new_page, custom_page) {
|
||||
const navbar_data_tag = document.getElementById('cache-navbar-previous')
|
||||
const small_panel_data_tag = document.getElementById('cache-page-small-previous')
|
||||
|
||||
document.getElementById(`small_panel_${page_name}`).classList.remove('show')
|
||||
small_panel_data_tag.removeAttribute('content')
|
||||
|
||||
const previous = navbar_data_tag.getAttribute('content')
|
||||
|
||||
/* INFO: Disable icon on old state */
|
||||
const pre_input = document.getElementById(`n_${previous}`)
|
||||
const pre_background = document.getElementById(`nibg_${previous}`)
|
||||
|
||||
pre_input.removeAttribute('checked')
|
||||
pre_background.classList.remove('show')
|
||||
|
||||
/* INFO: Enable icon on new state */
|
||||
const curr_input = document.getElementById(`n_${new_page}`)
|
||||
const i_background = document.getElementById(`nibg_${new_page}`)
|
||||
|
||||
document.getElementById(`panel_${new_page}`).classList.toggle('show')
|
||||
curr_input.setAttribute('checked', '')
|
||||
i_background.classList.toggle('show')
|
||||
|
||||
navbar_data_tag.setAttribute('content', custom_page ? custom_page : 'settings')
|
||||
}
|
||||
34
webroot/js/switcher/fontChanger.js
Normal file
@@ -0,0 +1,34 @@
|
||||
const switcher = document.getElementById('font_switcher')
|
||||
const rootCss = document.querySelector(':root')
|
||||
|
||||
let sys_font = localStorage.getItem('/system/font')
|
||||
if (!sys_font) sys_font = setData('false')
|
||||
if (sys_font === 'true') {
|
||||
switcher.setAttribute('checked', '')
|
||||
|
||||
setSystemFont()
|
||||
}
|
||||
|
||||
switcher.addEventListener('click', () => {
|
||||
sys_font = setData(String(switcher.checked))
|
||||
|
||||
switcher.checked ? setSystemFont() : document.getElementById('font-tag').remove()
|
||||
})
|
||||
|
||||
function setSystemFont() {
|
||||
const headTag = document.getElementsByTagName('head')[0]
|
||||
const styleTag = document.createElement('style')
|
||||
|
||||
styleTag.id = 'font-tag'
|
||||
headTag.appendChild(styleTag)
|
||||
styleTag.innerHTML = `
|
||||
:root {
|
||||
--font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif
|
||||
}`
|
||||
}
|
||||
|
||||
function setData(mode) {
|
||||
localStorage.setItem('/system/font', mode)
|
||||
|
||||
return mode
|
||||
}
|
||||
41
webroot/js/themes/amoled.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import { setDarkNav } from './darkNavbar.js'
|
||||
|
||||
const rootCss = document.querySelector(':root')
|
||||
|
||||
/* INFO: Changes the icons to match the theme */
|
||||
const close_icons = document.getElementsByClassName('close_icon')
|
||||
const expand_icons = document.getElementsByClassName('expander')
|
||||
const sp_lang_close = document.getElementById('sp_lang_close')
|
||||
const sp_theme_close = document.getElementById('sp_theme_close')
|
||||
|
||||
export function setAmoled(chooseSet) {
|
||||
rootCss.style.setProperty('--background', '#000000')
|
||||
rootCss.style.setProperty('--font', '#d9d9d9')
|
||||
rootCss.style.setProperty('--desc', '#a9a9a9')
|
||||
rootCss.style.setProperty('--dim', '#0b0d0f')
|
||||
rootCss.style.setProperty('--icon', '#22292d')
|
||||
rootCss.style.setProperty('--icon-bc', '#171b1d')
|
||||
rootCss.style.setProperty('--desktop-navbar', '#111417')
|
||||
rootCss.style.setProperty('--desktop-navicon', '#1c2225')
|
||||
rootCss.style.setProperty('--button', 'var(--background)')
|
||||
|
||||
if (chooseSet) setData('amoled')
|
||||
|
||||
for (const close_icon of close_icons) {
|
||||
close_icon.innerHTML = '<img src="assets/close.svg">'
|
||||
}
|
||||
|
||||
for (const expand_icon of expand_icons) {
|
||||
expand_icon.innerHTML = '<img class="dimc" src="assets/expand.svg">'
|
||||
}
|
||||
|
||||
sp_lang_close.innerHTML = '<img src="./assets/back.svg"/>'
|
||||
sp_theme_close.innerHTML = '<img src="./assets/back.svg"/>'
|
||||
setDarkNav()
|
||||
}
|
||||
|
||||
function setData(mode) {
|
||||
localStorage.setItem('/system/theme', mode)
|
||||
|
||||
return mode
|
||||
}
|
||||
43
webroot/js/themes/dark.js
Normal file
@@ -0,0 +1,43 @@
|
||||
import { setDarkNav } from './darkNavbar.js'
|
||||
|
||||
const rootCss = document.querySelector(':root')
|
||||
|
||||
/* INFO: Changes the icons to match the theme */
|
||||
const close_icons = document.getElementsByClassName('close_icon')
|
||||
const expand_icons = document.getElementsByClassName('expander')
|
||||
const sp_lang_close = document.getElementById('sp_lang_close')
|
||||
const sp_theme_close = document.getElementById('sp_theme_close')
|
||||
const sp_errorh_close = document.getElementById('sp_errorh_close')
|
||||
|
||||
export function setDark(chooseSet) {
|
||||
rootCss.style.setProperty('--background', '#181c20')
|
||||
rootCss.style.setProperty('--font', '#ffffff')
|
||||
rootCss.style.setProperty('--desc', '#c9c9c9')
|
||||
rootCss.style.setProperty('--dim', '#1d2327')
|
||||
rootCss.style.setProperty('--icon', '#48565e')
|
||||
rootCss.style.setProperty('--icon-bc', '#313a3f')
|
||||
rootCss.style.setProperty('--desktop-navbar', '#252b31')
|
||||
rootCss.style.setProperty('--desktop-navicon', '#333d42')
|
||||
rootCss.style.setProperty('--button', 'var(--background)')
|
||||
|
||||
if (chooseSet) setData('dark')
|
||||
|
||||
for (const close_icon of close_icons) {
|
||||
close_icon.innerHTML = '<img src="assets/close.svg">'
|
||||
}
|
||||
|
||||
for (const expand_icon of expand_icons) {
|
||||
expand_icon.innerHTML = '<img class="dimc" src="assets/expand.svg">'
|
||||
}
|
||||
|
||||
sp_lang_close.innerHTML = '<img src="./assets/back.svg"/>'
|
||||
sp_theme_close.innerHTML = '<img src="./assets/back.svg"/>'
|
||||
sp_errorh_close.innerHTML = '<img src="./assets/back.svg"/>'
|
||||
setDarkNav()
|
||||
}
|
||||
|
||||
function setData(mode) {
|
||||
localStorage.setItem('/system/theme', mode)
|
||||
|
||||
return mode
|
||||
}
|
||||
6
webroot/js/themes/darkNavbar.js
Normal file
@@ -0,0 +1,6 @@
|
||||
export function setDarkNav() {
|
||||
document.getElementById('ni_home').classList.remove('light')
|
||||
document.getElementById('ni_modules').classList.remove('light')
|
||||
document.getElementById('ni_actions').classList.remove('light')
|
||||
document.getElementById('ni_settings').classList.remove('light')
|
||||
}
|
||||
48
webroot/js/themes/light.js
Normal file
@@ -0,0 +1,48 @@
|
||||
import {
|
||||
light_close_icon,
|
||||
light_expand_icon,
|
||||
light_page_exit_icon,
|
||||
} from './lightIcon.js'
|
||||
import { setLightNav } from './lightNavbar.js'
|
||||
|
||||
const rootCss = document.querySelector(':root')
|
||||
|
||||
/* INFO: Changes the icons to match the theme */
|
||||
const close_icons = document.getElementsByClassName('close_icon')
|
||||
const expand_icons = document.getElementsByClassName('expander')
|
||||
const sp_lang_close = document.getElementById('sp_lang_close')
|
||||
const sp_theme_close = document.getElementById('sp_theme_close')
|
||||
const sp_errorh_close = document.getElementById('sp_errorh_close')
|
||||
|
||||
export function setLight(chooseSet) {
|
||||
rootCss.style.setProperty('--background', '#f2f2f2')
|
||||
rootCss.style.setProperty('--font', '#181c20')
|
||||
rootCss.style.setProperty('--desc', '#484d53')
|
||||
rootCss.style.setProperty('--dim', '#e0e0e0')
|
||||
rootCss.style.setProperty('--icon', '#acacac')
|
||||
rootCss.style.setProperty('--desktop-navbar', '#fefefe')
|
||||
rootCss.style.setProperty('--desktop-navicon', '#eeeeee')
|
||||
rootCss.style.setProperty('--icon-bc', '#c9c9c9')
|
||||
rootCss.style.setProperty('--button', '#b3b3b3')
|
||||
|
||||
if (chooseSet) setData('light')
|
||||
|
||||
for (const close_icon of close_icons) {
|
||||
close_icon.innerHTML = light_close_icon
|
||||
}
|
||||
|
||||
for (const expand_icon of expand_icons) {
|
||||
expand_icon.innerHTML = light_expand_icon
|
||||
}
|
||||
|
||||
sp_lang_close.innerHTML = light_page_exit_icon
|
||||
sp_theme_close.innerHTML = light_page_exit_icon
|
||||
sp_errorh_close.innerHTML = light_page_exit_icon
|
||||
setLightNav()
|
||||
}
|
||||
|
||||
function setData(mode) {
|
||||
localStorage.setItem('/system/theme', mode)
|
||||
|
||||
return mode
|
||||
}
|
||||
20
webroot/js/themes/lightIcon.js
Normal file
@@ -0,0 +1,20 @@
|
||||
export const light_expand_icon = `
|
||||
<svg class="dimc" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#2c2c2c">
|
||||
<path d="m480-340 180-180-57-56-123 123-123-123-57 56 180 180Zm0 260q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/>
|
||||
</svg>
|
||||
`
|
||||
export const light_close_icon = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="40px" viewBox="0 -960 960 960" width="40px" fill="#2c2c2c">
|
||||
<path d="m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z"/>
|
||||
</svg>
|
||||
`
|
||||
export const light_logs_icon = `
|
||||
<svg class="dimc" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#2c2c2c">
|
||||
<path d="M480-280q17 0 28.5-11.5T520-320q0-17-11.5-28.5T480-360q-17 0-28.5 11.5T440-320q0 17 11.5 28.5T480-280Zm-40-160h80v-240h-80v240Zm40 360q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/>
|
||||
</svg>
|
||||
`
|
||||
export const light_page_exit_icon = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="36px" viewBox="0 -960 960 960" width="36px" fill="#2c2c2c">
|
||||
<path d="m287-446.67 240 240L480-160 160-480l320-320 47 46.67-240 240h513v66.66H287Z"/>
|
||||
</svg>
|
||||
`
|
||||
6
webroot/js/themes/lightNavbar.js
Normal file
@@ -0,0 +1,6 @@
|
||||
export function setLightNav() {
|
||||
document.getElementById('ni_home').classList.add('light')
|
||||
document.getElementById('ni_modules').classList.add('light')
|
||||
document.getElementById('ni_actions').classList.add('light')
|
||||
document.getElementById('ni_settings').classList.add('light')
|
||||
}
|
||||
41
webroot/js/themes/monochrome.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import { setDarkNav } from './darkNavbar.js'
|
||||
|
||||
const rootCss = document.querySelector(':root')
|
||||
|
||||
/* INFO: Changes the icons to match the theme */
|
||||
const close_icons = document.getElementsByClassName('close_icon')
|
||||
const expand_icons = document.getElementsByClassName('expander')
|
||||
const sp_lang_close = document.getElementById('sp_lang_close')
|
||||
const sp_theme_close = document.getElementById('sp_theme_close')
|
||||
|
||||
export function setMonochrome(chooseSet) {
|
||||
rootCss.style.setProperty('--background', '#141414')
|
||||
rootCss.style.setProperty('--font', '#ffffff')
|
||||
rootCss.style.setProperty('--desc', '#c9c9c9')
|
||||
rootCss.style.setProperty('--dim', '#1c1c1c')
|
||||
rootCss.style.setProperty('--icon', '#494949')
|
||||
rootCss.style.setProperty('--icon-bc', '#292929')
|
||||
rootCss.style.setProperty('--desktop-navbar', '#252525')
|
||||
rootCss.style.setProperty('--desktop-navicon', '#3a3a3a')
|
||||
rootCss.style.setProperty('--button', 'var(--background)')
|
||||
|
||||
if (chooseSet) setData('monochrome')
|
||||
|
||||
for (const close_icon of close_icons) {
|
||||
close_icon.innerHTML = '<img src="assets/close.svg">'
|
||||
}
|
||||
|
||||
for (const expand_icon of expand_icons) {
|
||||
expand_icon.innerHTML = '<img class="dimc" src="assets/expand.svg">'
|
||||
}
|
||||
|
||||
sp_lang_close.innerHTML = '<img src="./assets/back.svg"/>'
|
||||
sp_theme_close.innerHTML = '<img src="./assets/back.svg"/>'
|
||||
setDarkNav()
|
||||
}
|
||||
|
||||
function setData(mode) {
|
||||
localStorage.setItem('/system/theme', mode)
|
||||
|
||||
return mode
|
||||
}
|
||||
38
webroot/js/translate/actions.js
Normal file
@@ -0,0 +1,38 @@
|
||||
export function translateActionsPage(old_translations, new_translations) {
|
||||
/* INFO: actions card */
|
||||
document.getElementById('panel_actions_header').innerHTML = new_translations.page.actions.header
|
||||
|
||||
/* INFO: monitor small card */
|
||||
document.getElementById('monitor_title').innerHTML = new_translations.page.actions.monitor
|
||||
if (document.getElementById('monitor_stop_button')) { /* INFO: Not all devices have 32-bit support */
|
||||
document.getElementById('monitor_stop_button').innerHTML = new_translations.page.actions.monitorButton.stop
|
||||
document.getElementById('monitor_start_button').innerHTML = new_translations.page.actions.monitorButton.start
|
||||
document.getElementById('monitor_pause_button').innerHTML = new_translations.page.actions.monitorButton.pause
|
||||
}
|
||||
|
||||
/* INFO: monitor status card */
|
||||
const monitor_status = document.getElementById('monitor_status')
|
||||
switch (monitor_status.innerHTML.replace(/(\r\n|\n|\r)/gm, '').trim()) {
|
||||
case old_translations.page.actions.status.tracing: {
|
||||
monitor_status.innerHTML = new_translations.page.actions.status.tracing
|
||||
|
||||
break
|
||||
}
|
||||
case old_translations.page.actions.status.stopping: {
|
||||
monitor_status.innerHTML = new_translations.page.actions.status.stopping
|
||||
|
||||
break
|
||||
}
|
||||
case old_translations.page.actions.status.stopped: {
|
||||
monitor_status.innerHTML = new_translations.page.actions.status.stopped
|
||||
|
||||
break
|
||||
}
|
||||
case old_translations.page.actions.status.exiting: {
|
||||
monitor_status.innerHTML = new_translations.page.actions.status.exiting
|
||||
|
||||
break
|
||||
}
|
||||
default: monitor_status.innerHTML = new_translations.page.actions.status.unknown
|
||||
}
|
||||
}
|
||||
90
webroot/js/translate/home.js
Normal file
@@ -0,0 +1,90 @@
|
||||
export function translateHomePage(old_translations, new_translations) {
|
||||
/* INFO: Translate variables */
|
||||
const rezygisk_state = document.getElementById('rezygisk_state')
|
||||
const zygote32_status_div = document.getElementById('zygote32_status')
|
||||
const zygote64_status_div = document.getElementById('zygote64_status')
|
||||
|
||||
switch (rezygisk_state.innerHTML.replace(/(\r\n|\n|\r)/gm, '').trim()) {
|
||||
case old_translations.page.home.status.ok: {
|
||||
rezygisk_state.innerHTML = new_translations.page.home.status.ok
|
||||
|
||||
break
|
||||
}
|
||||
case old_translations.page.home.status.partially: {
|
||||
rezygisk_state.innerHTML = new_translations.page.home.status.partially
|
||||
|
||||
break
|
||||
}
|
||||
case old_translations.page.home.status.notWorking: {
|
||||
rezygisk_state.innerHTML = new_translations.page.home.status.notWorking
|
||||
|
||||
break
|
||||
}
|
||||
case old_translations.page.home.status.unknown: {
|
||||
rezygisk_state.innerHTML = new_translations.global.unknown
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (zygote32_status_div) {
|
||||
switch (zygote32_status_div.innerHTML.replace(/(\r\n|\n|\r)/gm, '').trim()) {
|
||||
case old_translations.page.home.info.zygote.injected: {
|
||||
zygote32_status_div.innerHTML = new_translations.page.home.info.zygote.injected
|
||||
|
||||
break
|
||||
}
|
||||
case old_translations.page.home.info.zygote.notInjected: {
|
||||
zygote32_status_div.innerHTML = new_translations.page.home.info.zygote.notInjected
|
||||
|
||||
break
|
||||
}
|
||||
case old_translations.page.home.info.zygote.unknown: {
|
||||
zygote32_status_div.innerHTML = new_translations.global.unknown
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (zygote64_status_div) {
|
||||
switch (zygote64_status_div.innerHTML.replace(/(\r\n|\n|\r)/gm, '').trim()) {
|
||||
case old_translations.page.home.info.zygote.injected: {
|
||||
zygote64_status_div.innerHTML = new_translations.page.home.info.zygote.injected
|
||||
|
||||
break
|
||||
}
|
||||
case old_translations.page.home.info.zygote.notInjected: {
|
||||
zygote64_status_div.innerHTML = new_translations.page.home.info.zygote.notInjected
|
||||
|
||||
break
|
||||
}
|
||||
case old_translations.page.home.info.zygote.unknown: {
|
||||
zygote64_status_div.innerHTML = new_translations.global.unknown
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const android_version_div = document.getElementById('android_version_div')
|
||||
if (android_version_div.innerHTML === old_translations.global.unknown)
|
||||
android_version_div.innerHTML = new_translations.global.unknown
|
||||
|
||||
const kernel_version_div = document.getElementById('kernel_version_div')
|
||||
if (kernel_version_div.innerHTML === old_translations.global.unknown)
|
||||
kernel_version_div.innerHTML = new_translations.global.unknown
|
||||
|
||||
/* INFO: info card */
|
||||
document.getElementById('version_info_title').innerHTML = new_translations.page.home.info.version
|
||||
document.getElementById('root_info_title').innerHTML = new_translations.page.home.info.root
|
||||
|
||||
// const version_code = document.getElementById('version_code')
|
||||
// const root_impl = document.getElementById('root_impl')
|
||||
|
||||
// if (version_code.innerHTML.replace(/(\r\n|\n|\r)/gm, '').trim() === old_translations.global.unknown)
|
||||
// version_code.innerHTML = new_translations.global.unknown
|
||||
|
||||
// if (root_impl.innerHTML.replace(/(\r\n|\n|\r)/gm, '').trim() === old_translations.global.unknown)
|
||||
// root_impl.innerHTML = new_translations.global.unknown
|
||||
}
|
||||
10
webroot/js/translate/modules.js
Normal file
@@ -0,0 +1,10 @@
|
||||
export function translateModulesPage(new_translations) {
|
||||
document.getElementById('panel_modules_header').innerHTML = new_translations.page.modules.header
|
||||
document.getElementById('modules_list_not_avaliable').innerHTML = new_translations.page.modules.notAvaliable
|
||||
|
||||
/* INFO: arch type */
|
||||
const module_element_arch = document.getElementsByClassName('arch_desc')
|
||||
for (const module of module_element_arch) {
|
||||
module.innerHTML = new_translations.page.modules.arch
|
||||
}
|
||||
}
|
||||
27
webroot/js/translate/settings.js
Normal file
@@ -0,0 +1,27 @@
|
||||
export function translateSettingsPage(new_translations) {
|
||||
document.getElementById('panel_settings_header').innerHTML = new_translations.page.settings.header
|
||||
|
||||
/* INFO: Change font option */
|
||||
document.getElementById('sys_font_option_title').innerHTML = new_translations.page.settings.font.header
|
||||
document.getElementById('sys_font_option_desc').innerHTML = new_translations.page.settings.font.description
|
||||
|
||||
/* INFO: Change font option */
|
||||
document.getElementById('sys_theme_option_title').innerHTML = new_translations.page.settings.theme.header
|
||||
document.getElementById('sys_theme_option_desc').innerHTML = new_translations.page.settings.theme.description
|
||||
|
||||
/* INFO: Change font option */
|
||||
document.getElementById('sys_lang_option_title').innerHTML = new_translations.page.settings.language.header
|
||||
document.getElementById('sys_lang_option_desc').innerHTML = new_translations.page.settings.language.description
|
||||
|
||||
/* INFO: Change font option */
|
||||
document.getElementById('sys_errorh_title').innerHTML = new_translations.page.settings.logs.header
|
||||
document.getElementById('sys_errorh_desc').innerHTML = new_translations.page.settings.logs.description
|
||||
|
||||
/* INFO: Credit card */
|
||||
document.getElementById('mcre_title').innerHTML = new_translations.page.settings.credits.module
|
||||
document.getElementById('omcre_title').innerHTML = new_translations.page.settings.credits.original
|
||||
document.getElementById('webcre_title').innerHTML = new_translations.page.settings.credits.web
|
||||
/* INFO: License card */
|
||||
document.getElementById('mlic_title').innerHTML = new_translations.page.settings.license.module
|
||||
document.getElementById('mweb_title').innerHTML = new_translations.page.settings.license.web
|
||||
}
|
||||
93
webroot/lang/ar_EG.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": "مدير الروت",
|
||||
"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": "اختر الثيم المناسب"
|
||||
},
|
||||
"language": {
|
||||
"header": "تغيير اللغة",
|
||||
"description": "غير إلى لغة أخرى"
|
||||
},
|
||||
"logs": {
|
||||
"header": "سجل الأخطاء",
|
||||
"description": "إعرض كل سجلات الأخطاء"
|
||||
},
|
||||
"credits": {
|
||||
"module": "مطور الإضافة",
|
||||
"original": "المطورون الأصليون",
|
||||
"web": "مطور واجهة الويب"
|
||||
},
|
||||
"license": {
|
||||
"module": "ترخيص الإضافة",
|
||||
"web": "ترخيص واجهة الويب"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
93
webroot/lang/de_DE.json
Normal file
@@ -0,0 +1,93 @@
|
||||
{
|
||||
"langName": "Deutsch",
|
||||
"global": {
|
||||
"unknown": "Unbekannt"
|
||||
},
|
||||
"smallPage": {
|
||||
"language": {
|
||||
"header": "Neue Sprache wählen"
|
||||
},
|
||||
"theme": {
|
||||
"header": "Neues Theme wählen",
|
||||
"dark": "Dunkel",
|
||||
"light": "Hell",
|
||||
"system": "System Basiert"
|
||||
},
|
||||
"errorh": {
|
||||
"buttons": {
|
||||
"copy": "Kopieren",
|
||||
"clear": "ALLE LOGS LÖSCHEN"
|
||||
},
|
||||
"header": "Fehler Verlauf",
|
||||
"placeholder": "Es wurde kein Fehlerprotokoll aufgezeichnet!"
|
||||
}
|
||||
},
|
||||
"page": {
|
||||
"home": {
|
||||
"header": "Startseite",
|
||||
"status": {
|
||||
"notWorking": "Funktioniert nicht",
|
||||
"ok": "Funktioniert",
|
||||
"partially": "Funktioniert zum Teil"
|
||||
},
|
||||
"info": {
|
||||
"version": "Version",
|
||||
"root": "Root Implementierung",
|
||||
"zygote": {
|
||||
"injected": "Injiziert",
|
||||
"notInjected": "Nicht Injiziert",
|
||||
"unknown": "Unbekannt"
|
||||
}
|
||||
}
|
||||
},
|
||||
"modules": {
|
||||
"header": "Module",
|
||||
"notAvaliable": "Kein Modul nutzt Zygisk.",
|
||||
"arch": "Architektur: "
|
||||
},
|
||||
"settings": {
|
||||
"header": "Einstellungen",
|
||||
"font": {
|
||||
"header": "Systemschriftart aktivieren",
|
||||
"description": "Diese Option aktiviert die Systemschriftart in der aktuellen WebUI. HINWEIS: Möglicherweise nicht kompatibel mit FlipFont"
|
||||
},
|
||||
"theme": {
|
||||
"header": "System Theme",
|
||||
"description": "Wählen Sie Ihr System-Theme für die aktuelle WebUI"
|
||||
},
|
||||
"language": {
|
||||
"header": "Sprache auswählen",
|
||||
"description": "Wechsel zu deiner neuen Sprache"
|
||||
},
|
||||
"logs": {
|
||||
"header": "Fehler Verlauf",
|
||||
"description": "Zeige den Fehler Verlauf"
|
||||
},
|
||||
"credits": {
|
||||
"module": "Modul Entwickler",
|
||||
"original": "Original Modul Entwickler",
|
||||
"web": "WebUI Entwickler"
|
||||
},
|
||||
"license": {
|
||||
"module": "Modul Lizenz",
|
||||
"web": "WebUI Lizenz"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"header": "Aktionen",
|
||||
"monitorButton": {
|
||||
"pause": "Pause",
|
||||
"stop": "Stopp",
|
||||
"start": "Start"
|
||||
},
|
||||
"monitor": "Überwachen",
|
||||
"status": {
|
||||
"tracing": "Nachverfolgung",
|
||||
"stopping": "Beenden",
|
||||
"stopped": "Beendet",
|
||||
"exiting": "Wird Beendet",
|
||||
"unknown": "Unbekannt"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
93
webroot/lang/en_US.json
Normal file
@@ -0,0 +1,93 @@
|
||||
{
|
||||
"langName": "English (United States)",
|
||||
"global": {
|
||||
"unknown": "Unknown"
|
||||
},
|
||||
"smallPage": {
|
||||
"language": {
|
||||
"header": "Choose your new language"
|
||||
},
|
||||
"theme": {
|
||||
"header": "Choose your new theme",
|
||||
"dark": "Dark",
|
||||
"light": "Light",
|
||||
"system": "System Based"
|
||||
},
|
||||
"errorh": {
|
||||
"buttons": {
|
||||
"copy": "COPY",
|
||||
"clear": "CLEAR ALL LOGS"
|
||||
},
|
||||
"header": "Error History",
|
||||
"placeholder": "No error log recorded here!"
|
||||
}
|
||||
},
|
||||
"page": {
|
||||
"home": {
|
||||
"header": "Home",
|
||||
"status": {
|
||||
"notWorking": "Not Working",
|
||||
"ok": "Working",
|
||||
"partially": "Partially Working"
|
||||
},
|
||||
"info": {
|
||||
"version": "Version",
|
||||
"root": "Root Implementation",
|
||||
"zygote": {
|
||||
"injected": "Injected",
|
||||
"notInjected": "Not Injected",
|
||||
"unknown": "Unknown"
|
||||
}
|
||||
}
|
||||
},
|
||||
"modules": {
|
||||
"header": "Modules",
|
||||
"notAvaliable": "No modules using Zygisk here.",
|
||||
"arch": "Architecture: "
|
||||
},
|
||||
"actions": {
|
||||
"header": "Action",
|
||||
"monitorButton": {
|
||||
"start": "Start",
|
||||
"stop": "Stop",
|
||||
"pause": "Pause"
|
||||
},
|
||||
"monitor": "Monitor",
|
||||
"status": {
|
||||
"tracing": "Tracing",
|
||||
"stopping": "Stopping",
|
||||
"stopped": "Stopped",
|
||||
"exiting": "Exiting",
|
||||
"unknown": "Unknown"
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"header": "Settings",
|
||||
"font": {
|
||||
"header": "Enable system font",
|
||||
"description": "This option will enable system font in current WebUI. NOTE: May not be compatible with FlipFont"
|
||||
},
|
||||
"theme": {
|
||||
"header": "System theme",
|
||||
"description": "Choose your system theme for current WebUI"
|
||||
},
|
||||
"language": {
|
||||
"header": "Change language",
|
||||
"description": "Change to your new language"
|
||||
},
|
||||
"logs": {
|
||||
"header": "Error History",
|
||||
"description": "View all of your error log"
|
||||
},
|
||||
"credits": {
|
||||
"module": "Module Developer",
|
||||
"original": "Original Module Developer",
|
||||
"web": "WebUI Developer"
|
||||
},
|
||||
"license": {
|
||||
"module": "Module License",
|
||||
"web": "WebUI License"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
93
webroot/lang/es_AR.json
Normal file
@@ -0,0 +1,93 @@
|
||||
{
|
||||
"langName": "Español",
|
||||
"global": {
|
||||
"unknown": "Desconocido"
|
||||
},
|
||||
"smallPage": {
|
||||
"language": {
|
||||
"header": "Selecciona tu nuevo idioma"
|
||||
},
|
||||
"theme": {
|
||||
"header": "Elige tu nuevo tema",
|
||||
"dark": "Oscuro",
|
||||
"light": "Claro",
|
||||
"system": "Basado en el sistema"
|
||||
},
|
||||
"errorh": {
|
||||
"buttons": {
|
||||
"copy": "COPIAR",
|
||||
"clear": "BORRAR TODOS LOS REGISTROS"
|
||||
},
|
||||
"header": "Historial de errores",
|
||||
"placeholder": "¡No se registró ningún error aquí!"
|
||||
}
|
||||
},
|
||||
"page": {
|
||||
"home": {
|
||||
"header": "Inicio",
|
||||
"status": {
|
||||
"notWorking": "No funciona",
|
||||
"ok": "Funcionando",
|
||||
"partially": "Funciona parcialmente"
|
||||
},
|
||||
"info": {
|
||||
"version": "Versión",
|
||||
"root": "Implementación de root",
|
||||
"zygote": {
|
||||
"injected": "Inyectado",
|
||||
"notInjected": "No inyectado",
|
||||
"unknown": "Desconocido"
|
||||
}
|
||||
}
|
||||
},
|
||||
"modules": {
|
||||
"header": "Módulos",
|
||||
"notAvaliable": "No hay módulos que usen Zygisk aquí.",
|
||||
"arch": "Arquitectura: "
|
||||
},
|
||||
"settings": {
|
||||
"header": "Ajustes",
|
||||
"font": {
|
||||
"header": "Activar fuente del sistema",
|
||||
"description": "Esta opción habilitará la fuente del sistema en el WebUI actual. NOTA: Puede no ser compatible con FlipFont"
|
||||
},
|
||||
"theme": {
|
||||
"header": "Tema del sistema",
|
||||
"description": "Elija el tema del sistema para el WebUI actual"
|
||||
},
|
||||
"language": {
|
||||
"header": "Cambiar idioma",
|
||||
"description": "Selecciona tu nuevo idioma"
|
||||
},
|
||||
"logs": {
|
||||
"header": "Historial de errores",
|
||||
"description": "Ver todos tus registros de errores"
|
||||
},
|
||||
"credits": {
|
||||
"module": "Desarrollador del módulo",
|
||||
"original": "Desarrollador original del módulo",
|
||||
"web": "Desarrollador de la WebUI"
|
||||
},
|
||||
"license": {
|
||||
"module": "Licencia del módulo",
|
||||
"web": "Licencia de la WebUI"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"header": "Acciones",
|
||||
"monitorButton": {
|
||||
"pause": "Pausar",
|
||||
"stop": "Detener",
|
||||
"start": "Iniciar"
|
||||
},
|
||||
"monitor": "Monitor",
|
||||
"status": {
|
||||
"tracing": "Trazando",
|
||||
"stopping": "Deteniendo",
|
||||
"stopped": "Detenido",
|
||||
"exiting": "Saliendo",
|
||||
"unknown": "Desconocido"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
93
webroot/lang/es_ES.json
Normal file
@@ -0,0 +1,93 @@
|
||||
{
|
||||
"langName": "Español",
|
||||
"global": {
|
||||
"unknown": "Desconocido"
|
||||
},
|
||||
"smallPage": {
|
||||
"language": {
|
||||
"header": "Selecciona tu nuevo idioma"
|
||||
},
|
||||
"theme": {
|
||||
"header": "Elige tu nuevo tema",
|
||||
"dark": "Oscuro",
|
||||
"light": "Claro",
|
||||
"system": "Basado en el sistema"
|
||||
},
|
||||
"errorh": {
|
||||
"buttons": {
|
||||
"copy": "COPIAR",
|
||||
"clear": "BORRAR TODOS LOS REGISTROS"
|
||||
},
|
||||
"header": "Historial de errores",
|
||||
"placeholder": "¡No se registró ningún error aquí!"
|
||||
}
|
||||
},
|
||||
"page": {
|
||||
"home": {
|
||||
"header": "Inicio",
|
||||
"status": {
|
||||
"notWorking": "No funciona",
|
||||
"ok": "Funcionando",
|
||||
"partially": "Funciona parcialmente"
|
||||
},
|
||||
"info": {
|
||||
"version": "Versión",
|
||||
"root": "Implementación de root",
|
||||
"zygote": {
|
||||
"injected": "Inyectado",
|
||||
"notInjected": "No inyectado",
|
||||
"unknown": "Desconocido"
|
||||
}
|
||||
}
|
||||
},
|
||||
"modules": {
|
||||
"header": "Módulos",
|
||||
"notAvaliable": "No hay módulos que usen Zygisk aquí.",
|
||||
"arch": "Arquitectura: "
|
||||
},
|
||||
"settings": {
|
||||
"header": "Ajustes",
|
||||
"font": {
|
||||
"header": "Activar fuente del sistema",
|
||||
"description": "Esta opción habilitará la fuente del sistema en el WebUI actual. NOTA: Puede no ser compatible con FlipFont"
|
||||
},
|
||||
"theme": {
|
||||
"header": "Tema del sistema",
|
||||
"description": "Elija el tema del sistema para el WebUI actual"
|
||||
},
|
||||
"language": {
|
||||
"header": "Cambiar idioma",
|
||||
"description": "Selecciona tu nuevo idioma"
|
||||
},
|
||||
"logs": {
|
||||
"header": "Historial de errores",
|
||||
"description": "Ver todos tus registros de errores"
|
||||
},
|
||||
"credits": {
|
||||
"module": "Desarrollador del módulo",
|
||||
"original": "Desarrollador original del módulo",
|
||||
"web": "Desarrollador de la WebUI"
|
||||
},
|
||||
"license": {
|
||||
"module": "Licencia del módulo",
|
||||
"web": "Licencia de la WebUI"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"header": "Acciones",
|
||||
"monitorButton": {
|
||||
"pause": "Pausar",
|
||||
"stop": "Detener",
|
||||
"start": "Iniciar"
|
||||
},
|
||||
"monitor": "Monitor",
|
||||
"status": {
|
||||
"tracing": "Trazando",
|
||||
"stopping": "Deteniendo",
|
||||
"stopped": "Detenido",
|
||||
"exiting": "Saliendo",
|
||||
"unknown": "Desconocido"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
93
webroot/lang/es_MX.json
Normal file
@@ -0,0 +1,93 @@
|
||||
{
|
||||
"langName": "Español (México)",
|
||||
"global": {
|
||||
"unknown": "Desconocido"
|
||||
},
|
||||
"smallPage": {
|
||||
"language": {
|
||||
"header": "Elige tu nuevo tema"
|
||||
},
|
||||
"theme": {
|
||||
"header": "Elige tu nuevo tema",
|
||||
"dark": "Oscuro",
|
||||
"light": "Claro",
|
||||
"system": "Basado en el sistema"
|
||||
},
|
||||
"errorh": {
|
||||
"buttons": {
|
||||
"copy": "COPIAR",
|
||||
"clear": "BORRAR TODOS LOS REGISTROS"
|
||||
},
|
||||
"header": "Historial de errores",
|
||||
"placeholder": "¡No se registró ningún error aquí!"
|
||||
}
|
||||
},
|
||||
"page": {
|
||||
"home": {
|
||||
"header": "Inicio",
|
||||
"status": {
|
||||
"notWorking": "No funcionando",
|
||||
"ok": "Funcionando",
|
||||
"partially": "Funcionando parcialmente"
|
||||
},
|
||||
"info": {
|
||||
"version": "Versión",
|
||||
"root": "Implementación de root",
|
||||
"zygote": {
|
||||
"injected": "Inyectado",
|
||||
"notInjected": "No inyectado",
|
||||
"unknown": "Desconocido"
|
||||
}
|
||||
}
|
||||
},
|
||||
"modules": {
|
||||
"header": "Módulos",
|
||||
"notAvaliable": "No hay módulos que usen Zygisk aquí.",
|
||||
"arch": "Arquitectura: "
|
||||
},
|
||||
"settings": {
|
||||
"header": "Ajustes",
|
||||
"font": {
|
||||
"header": "Activar fuente del sistema",
|
||||
"description": "Esta opción habilitará la fuente del sistema en el WebUI actual. NOTA: Puede no ser compatible con FlipFont"
|
||||
},
|
||||
"theme": {
|
||||
"header": "Tema del sistema",
|
||||
"description": "Elige el tema del sistema para el WebUI actual"
|
||||
},
|
||||
"language": {
|
||||
"header": "Cambiar idioma",
|
||||
"description": "Cambia a tu nuevo idioma."
|
||||
},
|
||||
"logs": {
|
||||
"header": "Historial de errores",
|
||||
"description": "Ver todos tus registros de errores"
|
||||
},
|
||||
"credits": {
|
||||
"module": "Desarrollador del módulo",
|
||||
"original": "Desarrollador original del módulo",
|
||||
"web": "Desarrollador de la WebUI"
|
||||
},
|
||||
"license": {
|
||||
"module": "Licencia del módulo",
|
||||
"web": "Licencia de la WebUI"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"header": "Acciones",
|
||||
"monitorButton": {
|
||||
"pause": "Pausar",
|
||||
"stop": "Detener",
|
||||
"start": "Iniciar"
|
||||
},
|
||||
"monitor": "Monitor",
|
||||
"status": {
|
||||
"tracing": "Trazando",
|
||||
"stopping": "Deteniendo",
|
||||
"stopped": "Detenido",
|
||||
"exiting": "Saliendo",
|
||||
"unknown": "Desconocido"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
93
webroot/lang/fr_FR.json
Normal file
@@ -0,0 +1,93 @@
|
||||
{
|
||||
"langName": "Français (France)",
|
||||
"global": {
|
||||
"unknown": "Inconnu"
|
||||
},
|
||||
"smallPage": {
|
||||
"language": {
|
||||
"header": "Choisissez votre nouvelle langue"
|
||||
},
|
||||
"theme": {
|
||||
"header": "Choisissez votre nouveau thème",
|
||||
"dark": "Sombre",
|
||||
"light": "Clair",
|
||||
"system": "Basé sur le système"
|
||||
},
|
||||
"errorh": {
|
||||
"buttons": {
|
||||
"copy": "COPIER",
|
||||
"clear": "EFFACER TOUS LES JOURNEAUX"
|
||||
},
|
||||
"header": "Historique d'erreurs",
|
||||
"placeholder": "Pas de journal d'erreur enregistré ici !"
|
||||
}
|
||||
},
|
||||
"page": {
|
||||
"home": {
|
||||
"header": "Page d'accueil",
|
||||
"status": {
|
||||
"notWorking": "Inactif",
|
||||
"ok": "Actif",
|
||||
"partially": "Fonctionnement partiel"
|
||||
},
|
||||
"info": {
|
||||
"version": "Version",
|
||||
"root": "Implémentation root",
|
||||
"zygote": {
|
||||
"injected": "Injecté",
|
||||
"notInjected": "Non injecté",
|
||||
"unknown": "Inconnu"
|
||||
}
|
||||
}
|
||||
},
|
||||
"modules": {
|
||||
"header": "Modules",
|
||||
"notAvaliable": "Aucuns modules n'utilisent Zygisk actuellement.",
|
||||
"arch": " Architecture: "
|
||||
},
|
||||
"settings": {
|
||||
"header": "Paramètres",
|
||||
"font": {
|
||||
"header": "Activer la police du système",
|
||||
"description": "Cette option activera la police de caractère du système d'interface WebUI. NOTE: N'est peut-être pas compatible avec FlipFont"
|
||||
},
|
||||
"theme": {
|
||||
"header": "Thème système",
|
||||
"description": "Choisissez votre thème système pour le WebUI"
|
||||
},
|
||||
"language": {
|
||||
"header": "Changez de langue",
|
||||
"description": "Changez vers votre nouvelle langue"
|
||||
},
|
||||
"logs": {
|
||||
"header": "Historique d'erreur",
|
||||
"description": "Voir le détail du journal d'erreur"
|
||||
},
|
||||
"credits": {
|
||||
"module": "Module développeur",
|
||||
"original": "Module Original du Développeur",
|
||||
"web": "WebUI Développeur"
|
||||
},
|
||||
"license": {
|
||||
"module": "Module de licence",
|
||||
"web": "Licence WebUI"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"header": "Actions",
|
||||
"monitorButton": {
|
||||
"pause": "Mettre en Pause",
|
||||
"stop": "Arrêter",
|
||||
"start": "Démarrer"
|
||||
},
|
||||
"monitor": "Surveillance",
|
||||
"status": {
|
||||
"tracing": "Tracer",
|
||||
"stopping": "Stopper immédiatement",
|
||||
"stopped": "Arrêté",
|
||||
"exiting": "Sortir d'ici",
|
||||
"unknown": "Inconnu"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
93
webroot/lang/id_ID.json
Normal file
@@ -0,0 +1,93 @@
|
||||
{
|
||||
"langName": "Bahasa Indonesia (Indonesia)",
|
||||
"global": {
|
||||
"unknown": "Tidak diketahui"
|
||||
},
|
||||
"smallPage": {
|
||||
"language": {
|
||||
"header": "Pilih bahasa baru Anda"
|
||||
},
|
||||
"theme": {
|
||||
"header": "Pilih tema baru Anda",
|
||||
"dark": "Gelap",
|
||||
"light": "Terang",
|
||||
"system": "Bawaan Sistem"
|
||||
},
|
||||
"errorh": {
|
||||
"buttons": {
|
||||
"copy": "SALIN",
|
||||
"clear": "BERSIHKAN SEMUA LOG"
|
||||
},
|
||||
"header": "Log Error",
|
||||
"placeholder": "Tidak ada log error tercatat disini!"
|
||||
}
|
||||
},
|
||||
"page": {
|
||||
"home": {
|
||||
"header": "Beranda",
|
||||
"status": {
|
||||
"notWorking": "Tidak Bekerja",
|
||||
"ok": "Bekerja",
|
||||
"partially": "Bekerja Sebagian"
|
||||
},
|
||||
"info": {
|
||||
"version": "Versi",
|
||||
"root": "Implementasi Root",
|
||||
"zygote": {
|
||||
"injected": "Terinjeksi",
|
||||
"notInjected": "Tidak Terinjeksi",
|
||||
"unknown": "Tidak diketahui"
|
||||
}
|
||||
}
|
||||
},
|
||||
"modules": {
|
||||
"header": "Modul",
|
||||
"notAvaliable": "Tidak ada modul yang menggunakan Zygisk disini.",
|
||||
"arch": "Arsitektur: "
|
||||
},
|
||||
"settings": {
|
||||
"header": "Pengaturan",
|
||||
"font": {
|
||||
"header": "Gunakan Font Sistem",
|
||||
"description": "Opsi ini akan mengaktifkan font sistem pada WebUI saat ini. (Mungkin tidak kompatibel dengan FlipFont)"
|
||||
},
|
||||
"theme": {
|
||||
"header": "Tema Sistem",
|
||||
"description": "Pilih tema untuk tampilan WebUI Anda saat ini"
|
||||
},
|
||||
"language": {
|
||||
"header": "Ubah Bahasa",
|
||||
"description": "Ubah ke bahasa baru Anda"
|
||||
},
|
||||
"logs": {
|
||||
"header": "Log Error",
|
||||
"description": "Lihat semua log Error Anda"
|
||||
},
|
||||
"credits": {
|
||||
"module": "Developer Modul",
|
||||
"original": "Developer Modul Asli",
|
||||
"web": "Developer WebUI"
|
||||
},
|
||||
"license": {
|
||||
"module": "Lisensi Modul",
|
||||
"web": "Lisensi WebUI"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"header": "Tindakan",
|
||||
"monitorButton": {
|
||||
"pause": "Jeda",
|
||||
"stop": "Berhenti",
|
||||
"start": "Mulai"
|
||||
},
|
||||
"monitor": "Monitor",
|
||||
"status": {
|
||||
"tracing": "Melacak",
|
||||
"stopping": "Menghentikan",
|
||||
"stopped": "Terhenti",
|
||||
"exiting": "Terkeluar",
|
||||
"unknown": "Tidak diketahui"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
93
webroot/lang/it_IT.json
Normal file
@@ -0,0 +1,93 @@
|
||||
{
|
||||
"langName": "Italiano (Italia)",
|
||||
"global": {
|
||||
"unknown": "Sconosciuto"
|
||||
},
|
||||
"smallPage": {
|
||||
"language": {
|
||||
"header": "Scegli la tua nuova lingua"
|
||||
},
|
||||
"theme": {
|
||||
"header": "Scegli il tuo nuovo tema",
|
||||
"dark": "Scuro",
|
||||
"light": "Chiaro",
|
||||
"system": "Sistema"
|
||||
},
|
||||
"errorh": {
|
||||
"buttons": {
|
||||
"copy": "COPIA",
|
||||
"clear": "CANCELLA TUTTI I LOGS"
|
||||
},
|
||||
"header": "Storico degli Errori",
|
||||
"placeholder": "Nessun errore è stato registrato qui!"
|
||||
}
|
||||
},
|
||||
"page": {
|
||||
"home": {
|
||||
"header": "Home",
|
||||
"status": {
|
||||
"notWorking": "Non funzionante",
|
||||
"ok": "Funzionante",
|
||||
"partially": "Funzionante Parzialmente"
|
||||
},
|
||||
"info": {
|
||||
"version": "Versione",
|
||||
"root": "Implementazione root",
|
||||
"zygote": {
|
||||
"injected": "Iniettato",
|
||||
"notInjected": "Non Iniettato",
|
||||
"unknown": "Sconosciuto"
|
||||
}
|
||||
}
|
||||
},
|
||||
"modules": {
|
||||
"header": "Moduli",
|
||||
"notAvaliable": "Nessun modulo che utilizza Zygisk qui.",
|
||||
"arch": "Architettura: "
|
||||
},
|
||||
"settings": {
|
||||
"header": "Impostazioni",
|
||||
"font": {
|
||||
"header": "Abilita font di sistema",
|
||||
"description": "Questa opzione abiliterà il font di sistema nella WebUI. NOTA: Potrebbe non funzionare bene con FlipFont"
|
||||
},
|
||||
"theme": {
|
||||
"header": "Tema di sistema",
|
||||
"description": "Scegli il tuo tema di sistema per la WebUI"
|
||||
},
|
||||
"language": {
|
||||
"header": "Cambia lingua",
|
||||
"description": "Cambia alla tua nuova lingua"
|
||||
},
|
||||
"logs": {
|
||||
"header": "Storico degli errori",
|
||||
"description": "Visualizza tutto il log dell'errore"
|
||||
},
|
||||
"credits": {
|
||||
"module": "Programmatore del Modulo",
|
||||
"original": "Programmatore del Modulo Originale",
|
||||
"web": "Programmatore della WebUI"
|
||||
},
|
||||
"license": {
|
||||
"module": "Licenza del modulo",
|
||||
"web": "Licenza della WebUI"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"header": "Azioni",
|
||||
"monitorButton": {
|
||||
"pause": "Pausa",
|
||||
"stop": "Ferma",
|
||||
"start": "Avvia"
|
||||
},
|
||||
"monitor": "Controlla",
|
||||
"status": {
|
||||
"tracing": "Tracing",
|
||||
"stopping": "Fermando",
|
||||
"stopped": "Fermato",
|
||||
"exiting": "Uscendo",
|
||||
"unknown": "Sconosciuto"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||