52 Commits

Author SHA1 Message Date
ThePedroo
153097f9d8 update: rewrite ART method and module related code to C
This commit rewrites both ART method and module related code to C, following the same behavior, aside from the module's `on load` now be called globally, allowing a better flexibility. This will not impact any module.
2025-09-01 22:38:55 -03:00
ThePedroo
0f27e455e7 improve: preload modules globally
This commit makes Zygisk modules be preloaded globally in the main Zygote process, so that they don't need to reload all the time. This commit is authored by @nampud, and merged into mainline ReZygisk.

Co-authored-by: nampud
2025-08-24 14:19:08 -03:00
ThePedroo
3688df6450 fix: *AppSpecialize inconsistency for root manager process; improve: allow to inject into root manager
This commit fixes an issue where modules would have their "postAppSpecialize" called for root manager processes, but not "preAppSpecialize". The approach taken to fix that was to completely allow to inject into root manager, which instead of making it not call "postAppSpecialize" either, now it allows both.
2025-08-15 21:30:18 -03:00
ThePedroo
0c7a756030 update: LSPlt submodule
This commit updates the LSPlt submodule used in ReZygisk to the latest commit.
2025-08-15 06:30:48 -03:00
ThePedroo
70805bb390 fix: misinterpreted state.json Zygote injection information
This commit fixes the incorrect interpretation of the "state.json"'s Zygote injection information.
2025-08-13 22:06:04 -03:00
ThePedroo
e6344d2e12 add: r_debug_tail trace hiding for undlclosed modules
This commit adds support for hiding "r_debug_tail" trace when a module is not "dlclose"d in the app's process.
2025-08-13 21:43:46 -03:00
ThePedroo
d2ebb2bfed improve: ReZygisk state in module description
This commit improves how ReZygisk state is shown in the module description, reducing its size in favor of the same amount of information but in the WebUI, as all root implementations have WebUI available, even if it's through external applications due to incompetency, such as Magisk.

closes #28
2025-08-13 21:34:13 -03:00
ThePedroo
7e823319b7 improve: SoInfo hiding code complexity
This commit improves the SoInfo hiding code by reducing the complexity of it, using dlclose directly when possible to make it more future proof and simple.

Co-Authored-By: 4h9fbZ <176179231+4h9fbZ@users.noreply.github.com>
2025-08-10 18:18:26 -03:00
ThePedroo
f9fcf1c2e7 fix: remote strlen fail in dlopen path; add: GNU ifunc handling to elf_util.c
This commit fixes the remote call to "strlen" fail because ReZygisk ELF utils would not handle GNU indirect functions, making it call the in-between function instead of the actual function.
2025-08-10 17:21:34 -03:00
ThePedroo
08513b17e8 fix: memory leak, unitialized memory access, FILE pointer leak bugs
This commit fixes numerous general code bugs, improving reliability and consistency of ReZygisk.
2025-08-10 17:06:48 -03:00
ThePedroo
a7917e20fe update: LSPlt
This commit updates LSPlt and adapt to its changes so that it works properly.
2025-07-30 23:09:55 -03:00
ThePedroo
e0ce1473dd fix: crash when umounting preloaded file
This commit fixes an issue where Zygote will Abort when a preloaded file cannot be opened anymore, which happens when ReZygisk umounts a file from a module (such as Quick Switch Overlay or Pixelify) that is preloaded. To fix that, we hook on the function that causes the crash and bypass its execution if it cannot be opened.
2025-07-20 19:27:36 -03:00
ThePedroo
bf3c73d72b fix: not extracting any binaries in some devices
The commit fixes the issue that in devices that "ro.system.product.cpu.abilist" doesn't exist, it will cause ReZygisk "customize.sh" to not extract any binaries. Because of that, we'll fallback to the old "ro.product.cpu.abilist" when "ro.system.product.cpu.abilist" doesn't exist.
2025-07-20 14:10:56 -03:00
ThePedroo
510e8a2de4 fix: not extracting 32-bit binaries in systems using Tango
This commit fixes an issue where ReZygisk would not extract the 32-bit libraries in systems that utilize Tango (binary translation) to run 32-bit applications, as "ro.product.cpu.abilist" will only show the CPU supported architectures, not the system ones, resulting in only extracting 64-bit libzygisk.so and ReZygiskd despite having 32-bit app_process (Zygote).
2025-07-20 02:43:51 -03:00
ThePedroo
38cfbb25ef fix: Permission Denied on setns in 3.19- kernels
This commit fixes the issue where "setns" in older kernels will fail, as until 3.19 Linux used NSFS (Name Space File System) for namespaces (/proc/self/mnt/ns), which was not taken into consideration in ReZygisk SELinux rules.
2025-07-15 05:36:11 -03:00
ThePedroo
d54cac89a7 fix: missing #define for misc.h
This commit fixes the missing "#define", used to ensure a header symbols are not re-defined.
2025-07-15 05:20:59 -03:00
ThePedroo
90da42a10b fix: passing invalid address to reply_ok in KernelSU prctl
This commit fixes an issue that was caused by ReZygiskd passing NULL (an invalid address) in "reply_ok" to KernelSU driver, which expects it to be a valid address to pass if the request was successful, causing it to spam the dmesg with error logs.
2025-07-15 05:12:33 -03:00
ThePedroo
872ba693a1 remove: futile maps hiding
This commit removes the maps hiding, as it not only breaks behavior compatibility with Magisk Zygisk (original), but also can break some modules because of that. It doesn't provide any improved hiding in slightly better detections and makes the codebase more complex.
2025-07-15 03:41:48 -03:00
ThePedroo
2dfa221287 remove: unnecessary unshare hook
This commit removes the "unshare" hook. It used to provide the right timing for manual umount in the past, however with recent umount system changes, basing now on mount namespaces, those are handled way earlier, and this "unshare" hook makes no difference, being considered useless.
2025-07-15 03:31:01 -03:00
Rifat Azad
bc1b757bb8 update: CMD_HOOK_MODE and CMD_GET_MANAGER_UID values (#200)
This commit updates the values for "CMD_HOOK_MODE" to match newest KernelSU Next value and "CMD_GET_MANAGER_UID", now unified in both KernelSU and KernelSU Next.

Signed-off-by: Rifat Azad <33044977+rifsxd@users.noreply.github.com>
2025-07-11 09:32:17 -03:00
Re*Index. (ot_inc)
a0a54f2153 add: ja_JP translation to WebUI (#195)
This commit adds Japanese translation to ReZygisk WebUI.

Signed-off-by: Re*Index. (ot_inc) <32851879+reindex-ot@users.noreply.github.com>
2025-07-05 00:30:22 -03:00
Ada
295a62b649 update: tr_TR credits in TRANSLATOR.md (#192)
This commit updates the credits for "tr_TR" translator, updating witchfuneral's GitHub account name.

Signed-off-by: Ada <65596971+witchfuneral@users.noreply.github.com>
2025-07-05 00:25:45 -03:00
ThePedroo
b6f02b39b3 fix: runtime KSUN manager switch detection
This commit fixes the issue where the new KernelSU Next manager wouldn't be recognized if it was changed without a reboot.
2025-07-01 14:31:06 -03:00
ThePedroo
e036b1f40a add: KernelSU Next spoofed manager recognition support
This commit adds support for ReZygiskd to identify which app is the manager in KSUN (KernelSU Next) builds with spoofed manager package name.
2025-07-01 14:11:39 -03:00
ThePedroo
9a3b2f4a79 fix: KernelSU variant detection
This commit fixes the KSU variant detection, which previously was hardcoded on accident to always set it to KernelSU Next.
2025-06-29 14:23:04 -03:00
nampud
9810eb3974 improve: ptrace_message reset with seccomp (#191)
This commit improves how "ptrace_message" is cleaned by utilizing seccomp (Secure Computation Mode) to clear its value.

"ptrace_message" not being cleared is a Linux kernel vulnerability/bug that impacts all versions below 6.1, as the fix only came in 6.1, and because of that, we need to find ways to "0" it. This is the second fix for that, being the GKI2 fix the first: 70697be9a5
2025-06-29 03:41:42 -03:00
ThePedroo
823623a96f improve: set ZYGISK_ENABLED in all supported managers' process
This commit sets the environment variable "ZYGISK_ENABLED" for all managers' process, not limiting to Magisk anymore, so that managers can easily detect (Re)Zygisk.
2025-06-27 14:39:36 -03:00
ThePedroo
a75b2fe2b8 add: KernelSU Next recognition support
This commit adds support for recognizing KernelSU (KSUN) manager to ReZygiskd.
2025-06-27 14:39:21 -03:00
ThePedroo
48238521df fix: root related mounts leak in KernelSU in isolated services
This commit fixes the leak of mounts in KernelSU with global umount disabled in isolated services. This happens because KernelSU doesn't handle isolated services in kernel side, so we must find the main app UID and see if that UID is in denylist instead. With that, also improve APatch detection to take advantage of faster integer/UID comparison rather than always check process/string comparison, and only fallback to process name based if UID is not found.

Co-Authored-By: nampud <nampud@users.noreply.github.com>
2025-06-24 19:21:26 -03:00
ThePedroo
fa9adcf3b5 fix: FORCE_DENYLIST_UNMOUNT not forcing umount
This commit fixes the issue where because the mount namespace switch happened only before the Zygisk modules execution, they wouldn't have the opportunity to set "FORCE_DENYLIST_UNMOUNT" flag. Now, with this commit, which added another check to know if that flag was set by a Zygisk module, and if so, switched to mount namespace, adjusts the behavior to the expected one.
2025-06-22 18:33:10 -03:00
ThePedroo
6c05527ffa fix: Zygisk modules not being recognized in WSA
This commit fixes the issue where due to ARM architectures having priority in ReZygiskd code, ReZygisk, running on emulators, wouldn't be capable of recognizing Zygisk modules. This order is important, Zygisk modules should always give priority to the native architecture over the emulated one, since WSA runs Zygote in x64/x86.
2025-06-22 17:09:37 -03:00
ThePedroo
aff2ad8d3c add: SIGPIPE handling in ReZygiskd
This commit adds "SIGPIPE" signal handling in ReZygiskd. Some processes might die while ReZygiskd is still processing the response, and it will fail to write to the reader, now dead, resulting in a "SIGPIPE". Without proper handling, the process (ReZygiskd) would die, and this commit properly handled it to gracefully log it instead of dying.
2025-06-22 02:10:43 -03:00
ThePedroo
b7fe7b3dbe fix: root related mounts leak in APatch in isolated services
This commit fixes the issue where mounts related to APatch and ReZygisk would be leaked in isolated services for APatch environments as the UID between the main process and isolated service is different, resulting it to not be found in "package_config" and default to not switch to clean mount namespace.
2025-06-21 14:29:03 -03:00
ThePedroo
f432550f07 improve: dynamically retrieve libc.so path
This commit makes ptracer dynamically get "libc.so" library path, avoiding errors when errors happen in environments where libc.so from zygote doesn't come from "/system/lib" or "/system/lib64".
2025-06-15 15:22:44 -03:00
nampud
a0ab02cedc fix: not dropping SoInfo of unclosed modules; fix: not performing maps spoofing (#187)
This commit fixes the issue where unclosed modules, as in not being requested to be "dlclose"d, wouldn't have their SoInfo structures freed. It also fixes the issue of maps spoofing not being performed due to the "spoof_maps" parameter being erroneously set to false in the second "clean_trace" call.
2025-06-15 09:47:54 -03:00
ThePedroo
f9a23a2882 fix: checking if find_containing_library exists and error'ing
This commit fixes the typo where ReZygisk would check if a function exists in linker, and if it did, not proceed instead of proceeding.

closes #184
2025-06-13 17:21:55 -03:00
nampud
d111a2dfc5 fix: zygote64 crashes due to perfetto by unloading earlier (#177)
This commit fixes the crashes in "zygote64" caused by libperfetto hooks (more information in #177) by unloading earlier.
2025-06-10 23:00:59 -03:00
ThePedroo
cd4784376e fix: ReZygiskd out-of-bounds write due to too small system_arch buffer
This commit fixes the issue where when the "ro.product.cpu.abilist" prop has a value bigger than 31 characters, which mostly happens in WSA (Windows Subsystem For Android) as it can support 5+ architectures, it will write outside the buffer, leading to undefined behavior, but most of the time crashes.
2025-06-10 19:32:35 -03:00
ThePedroo
c786790b0f fix: Trusted CI failing in forks
This commit fixes issue that due to the lack of private and private key in forks, the Trusted CI ends up failing.
2025-06-09 22:59:44 -03:00
ThePedroo
4f35e06ac4 fix: gradle related warnings
This commit fixes the warnings caused by using deprecated functions in ReZygisk building system.
2025-06-09 14:36:07 -03:00
ThePedroo
57f985292e fix: leak of dir fd in ReZygiskd
This commit fixes the leak of dir fd caused due to not calling "closedir" after dir being used.
2025-06-09 14:35:04 -03:00
ThePedroo
34643c794f improve: SoInfo hiding code compatibility
This commit improves the compatibility of SoInfo hiding code with more Android versions, like Android 16 QPR1 Beta 1 and newer versions, and also reduces complexity of the code.
2025-06-09 14:34:00 -03:00
ThePedroo
ec705fb260 fix: removal of all PLT hooks unconditionally
This commit fixes the issue where due to a confusion, ReZygisk was coded so that it would remove all PLT hooks, even if they were meant to be kept. This has been fixed appropriately in LSPlt side, allowing to revert back to how it was before.
2025-06-06 03:09:55 -03:00
ThePedroo
c023da0fd6 fix: Code of Conduct URL in templates
This commit fixes the URL of CoC which would previously point to the wrong place.
2025-06-02 17:08:50 -03:00
ThePedroo
63f29f0771 update: setup-gradle action
This commit updates the "setup-gradle" action to major 4 so that it isn't in a specific version.
2025-06-01 23:27:21 -03:00
ThePedroo
c975722795 update: PLT hooks unload
This commit changes how PLT hooks are unloaded, so that we're able to bypass detections caused by page faulting libandroid_runtime.so.
2025-06-01 23:21:43 -03:00
ThePedroo
2f589d0eda update: LSPlt source
This commit fixes the fork of LSPlt used in ReZygisk, from JingMatrix to PerformanC, for more control.
2025-06-01 23:21:24 -03:00
ThePedroo
70697be9a5 fix: ptrace_message leaking ReZygisk existence (zygote pid)
This commit fixes a trace left due to a kernel bug. In some cases (and all of them here), the sequence of events may lead to "ptrace_message" not be properly reset/not represent the actual state. This happens here, as when TRACEFORK is set in "monitor.c", setting "ptrace_message" as the PID of the new process, persists even when not tracing anymore, causing leaks.

This fix has been given by @nampud, in #171.
2025-05-27 19:32:20 -03:00
ThePedroo
6261466e44 fix: Zygisk module loading
This commit fixes the issue where ReZygisk would try to load Zygisk modules after umounting mounts, leading to them not being found anymore.
2025-05-25 01:10:25 -03:00
ThePedroo
d455117c49 fix: injection when libdl.so is not in apex
This commit fixes the issue where "libdl.so" wouldn't be found, when trying to inject "lizygisk.so", in systems where apex is in "/system/lib/libdl.so".
2025-05-23 12:27:15 -03:00
ThePedroo
6272e0a2ac improve: ReZygiskd umount code by unifying; improve: umount system by making umount if less strict
This commit both improves ReZygiskd umount system by unifying all root implementations code into a single one, and also by making it less strict to umount.
2025-05-20 14:25:39 -03:00
ThePedroo
62481ca2b6 fix: wrong logic for detecting leaked fds; add: leaked fd warning
This commit fixes the logic issue which made leaked fds not be closed by "libzygisk.so", causing crashes for some. It also adds a warning for when it finds a leaked fd, so that module developers can be notified.

fixes #163
2025-05-20 14:20:37 -03:00
53 changed files with 2362 additions and 2946 deletions

View File

@@ -59,7 +59,7 @@ body:
id: code_of_conduct id: code_of_conduct
attributes: attributes:
label: Code of Conduct 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: options:
- label: I agree to follow this project's Code of Conduct - label: I agree to follow this project's Code of Conduct
required: true required: true

View File

@@ -35,7 +35,7 @@ body:
id: code_of_conduct id: code_of_conduct
attributes: attributes:
label: Code of Conduct 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: options:
- label: I agree to follow this project's Code of Conduct - label: I agree to follow this project's Code of Conduct
required: true required: true

View File

@@ -22,7 +22,7 @@ jobs:
java-version: "17" java-version: "17"
- name: Setup Gradle - name: Setup Gradle
uses: gradle/actions/setup-gradle@v4.2.1 uses: gradle/actions/setup-gradle@v4
- name: Build with Gradle - name: Build with Gradle
run: | run: |

View File

@@ -21,15 +21,19 @@ jobs:
java-version: "17" java-version: "17"
- name: Setup Gradle - name: Setup Gradle
uses: gradle/actions/setup-gradle@v4.2.1 uses: gradle/actions/setup-gradle@v4
- name: Setup keys - name: Setup keys
env: env:
private_key: ${{ secrets.ORG_PRIVATE_KEY }} private_key: ${{ secrets.ORG_PRIVATE_KEY }}
public_key: ${{ secrets.ORG_PUBLIC_KEY }} public_key: ${{ secrets.ORG_PUBLIC_KEY }}
run: | run: |
echo "$private_key" | base64 -d > module/private_key if [ -z "$private_key" ] || [ -z "$public_key" ]; then
echo "$public_key" | base64 -d > module/public_key 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 - name: Build with Gradle
run: | run: |

4
.gitmodules vendored
View File

@@ -1,3 +1,3 @@
[submodule "loader/src/external/lsplt"] [submodule "LSPlt"]
path = loader/src/external/lsplt path = loader/src/external/lsplt
url = https://github.com/JingMatrix/LSPlt url = https://github.com/PerformanC/LSPlt

View File

@@ -6,13 +6,13 @@
- **es_ES** by [@LuchoModzzz](https://github.com/Lxchoooo) - **es_ES** by [@LuchoModzzz](https://github.com/Lxchoooo)
- **es_MX** by [@LuchoModzzz](https://github.com/Lxchoooo) - **es_MX** by [@LuchoModzzz](https://github.com/Lxchoooo)
- **fr_FR** by [@GhostFRR](https://github.com/GhostFRR) - **fr_FR** by [@GhostFRR](https://github.com/GhostFRR)
- **ja_JP** by [@Fyphen1223](https://github.com/Fyphen1223) - **ja_JP** by [@Fyphen1223](https://github.com/Fyphen1223) & [@reindex-ot](https://github.com/reindex-ot)
- **id_ID** by [@bpanca05](https://github.com/bpanca05) & [@LuckyKiddos](https://github.com/GuitarHeroStyles) - **id_ID** by [@bpanca05](https://github.com/bpanca05) & [@LuckyKiddos](https://github.com/GuitarHeroStyles)
- **it_IT** by [@thasave14](https://github.com/thasave14) - **it_IT** by [@thasave14](https://github.com/thasave14)
- **pt_BR** by [@ThePedroo](https://github.com/ThePedroo) - **pt_BR** by [@ThePedroo](https://github.com/ThePedroo)
- **ro_RO** by [@ExtremeXT](https://github.com/ExtremeXT) - **ro_RO** by [@ExtremeXT](https://github.com/ExtremeXT)
- **ru_RU** by [@Emulond](https://github.com/Emulond) & [@AJleKcAHgP68](https://github.com/AJleKcAHgP68) - **ru_RU** by [@Emulond](https://github.com/Emulond) & [@AJleKcAHgP68](https://github.com/AJleKcAHgP68)
- **tr_TR** by [@dyingwillow](https://github.com/dyingwillow) - **tr_TR** by [@witchfuneral](https://github.com/witchfuneral)
- **uk_UA** by [@Kittyskj](https://github.com/Kittyskj) - **uk_UA** by [@Kittyskj](https://github.com/Kittyskj)
- **vi_VN** by [@RainyXeon](https://github.com/RainyXeon) - **vi_VN** by [@RainyXeon](https://github.com/RainyXeon)
- **zh_CN** by [@Meltartica](https://github.com/Meltartica) & [@SheepChef](https://github.com/SheepChef) - **zh_CN** by [@Meltartica](https://github.com/Meltartica) & [@SheepChef](https://github.com/SheepChef)

View File

@@ -38,19 +38,18 @@ val androidSourceCompatibility by extra(JavaVersion.VERSION_11)
val androidTargetCompatibility by extra(JavaVersion.VERSION_11) val androidTargetCompatibility by extra(JavaVersion.VERSION_11)
tasks.register("Delete", Delete::class) { tasks.register("Delete", Delete::class) {
delete(rootProject.buildDir) delete(layout.buildDirectory.get())
} }
fun Project.configureBaseExtension() { fun Project.configureBaseExtension() {
extensions.findByType(LibraryExtension::class)?.run { extensions.findByType(LibraryExtension::class)?.run {
namespace = "icu.nullptr.zygisk.next" namespace = "com.performanc.org.rezygisk"
compileSdk = androidCompileSdkVersion compileSdk = androidCompileSdkVersion
ndkVersion = androidCompileNdkVersion ndkVersion = androidCompileNdkVersion
buildToolsVersion = androidBuildToolsVersion buildToolsVersion = androidBuildToolsVersion
defaultConfig { defaultConfig {
minSdk = androidMinSdkVersion minSdk = androidMinSdkVersion
targetSdk = androidTargetSdkVersion
} }
lint { lint {

View File

@@ -57,7 +57,6 @@ android {
defaultConfig { defaultConfig {
externalNativeBuild.cmake { externalNativeBuild.cmake {
arguments += "-DANDROID_STL=none" arguments += "-DANDROID_STL=none"
arguments += "-DLSPLT_STANDALONE=ON"
arguments += "-DCMAKE_BUILD_PARALLEL_LEVEL=${Runtime.getRuntime().availableProcessors()}" arguments += "-DCMAKE_BUILD_PARALLEL_LEVEL=${Runtime.getRuntime().availableProcessors()}"
cFlags("-std=c18", *defaultCFlags) cFlags("-std=c18", *defaultCFlags)
cppFlags("-std=c++20", *defaultCFlags) cppFlags("-std=c++20", *defaultCFlags)

View File

@@ -15,7 +15,7 @@ target_link_libraries(common log)
aux_source_directory(injector INJECTOR_SRC_LIST) aux_source_directory(injector INJECTOR_SRC_LIST)
add_library(zygisk SHARED ${INJECTOR_SRC_LIST}) add_library(zygisk SHARED ${INJECTOR_SRC_LIST})
target_include_directories(zygisk PRIVATE include) 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) aux_source_directory(ptracer PTRACER_SRC_LIST)
add_executable(libzygisk_ptrace.so ${PTRACER_SRC_LIST}) add_executable(libzygisk_ptrace.so ${PTRACER_SRC_LIST})

View File

@@ -108,43 +108,32 @@ void rezygiskd_get_info(struct rezygisk_info *info) {
read_uint32_t(fd, (uint32_t *)&info->pid); read_uint32_t(fd, (uint32_t *)&info->pid);
read_size_t(fd, &info->modules->modules_count); read_size_t(fd, &info->modules.modules_count);
if (info->modules->modules_count == 0) { if (info->modules.modules_count == 0) {
info->modules->modules = NULL; info->modules.modules = NULL;
close(fd); close(fd);
return; return;
} }
info->modules->modules = (char **)malloc(sizeof(char *) * info->modules->modules_count); info->modules.modules = (char **)malloc(sizeof(char *) * info->modules.modules_count);
if (info->modules->modules == NULL) { if (!info->modules.modules) {
PLOGE("allocating modules name memory"); PLOGE("allocating modules name memory");
free(info->modules); info->modules.modules_count = 0;
info->modules = NULL;
info->modules->modules_count = 0;
close(fd); close(fd);
return; 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); char *module_name = read_string(fd);
if (module_name == NULL) { if (module_name == NULL) {
PLOGE("reading module name"); PLOGE("reading module name");
info->modules->modules_count = i; goto info_cleanup;
free_rezygisk_info(info);
info->modules = NULL;
info->modules->modules_count = 0;
close(fd);
return;
} }
char module_path[PATH_MAX]; char module_path[PATH_MAX];
@@ -156,43 +145,49 @@ void rezygiskd_get_info(struct rezygisk_info *info) {
if (!module_prop) { if (!module_prop) {
PLOGE("failed to open module prop file %s", module_path); PLOGE("failed to open module prop file %s", module_path);
info->modules->modules_count = i; goto info_cleanup;
free_rezygisk_info(info);
info->modules = NULL;
info->modules->modules_count = 0;
close(fd);
return;
} }
info->modules.modules[i] = NULL;
char line[1024]; char line[1024];
while (fgets(line, sizeof(line), module_prop) != NULL) { while (fgets(line, sizeof(line), module_prop) != NULL) {
if (strncmp(line, "name=", strlen("name=")) != 0) continue; 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; 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); fclose(module_prop);
continue;
info_cleanup:
info->modules.modules_count = i;
free_rezygisk_info(info);
break;
} }
close(fd); close(fd);
} }
void free_rezygisk_info(struct rezygisk_info *info) { void free_rezygisk_info(struct rezygisk_info *info) {
if (info->modules->modules) { for (size_t i = 0; i < info->modules.modules_count; i++) {
for (size_t i = 0; i < info->modules->modules_count; i++) { free(info->modules.modules[i]);
free(info->modules->modules[i]);
}
free(info->modules->modules);
} }
free(info->modules); free(info->modules.modules);
info->modules.modules = NULL;
} }
bool rezygiskd_read_modules(struct zygisk_modules *modules) { 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) { void free_modules(struct zygisk_modules *modules) {
if (modules->modules) { for (size_t i = 0; i < modules->modules_count; i++) {
for (size_t i = 0; i < modules->modules_count; i++) { free(modules->modules[i]);
free(modules->modules[i]);
}
free(modules->modules);
} }
free(modules->modules);
} }
int rezygiskd_connect_companion(size_t index) { int rezygiskd_connect_companion(size_t index) {

View File

@@ -5,6 +5,7 @@
#include <fcntl.h> #include <fcntl.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/auxv.h>
#include <unistd.h> #include <unistd.h>
@@ -159,14 +160,14 @@ ElfImg *ElfImg_create(const char *elf, void *base) {
} }
if (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 fetched bases to be passed, as the linker (Android 7.1
and below) is not loaded from dlopen, which makes it not and below) is not loaded from dlopen, which makes it not
be visible with dl_iterate_phdr. be visible with dl_iterate_phdr.
*/ */
img->base = base; 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 { } else {
if (!_find_module_base(img)) { if (!_find_module_base(img)) {
LOGE("Failed to find module base for %s using dl_iterate_phdr", elf); 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; img->symstr_offset_for_symtab = 0;
} }
} else { } 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_start = NULL;
img->symtab_count = 0; 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; img->bias = phdr[i].p_vaddr - phdr[i].p_offset;
bias_calculated = true; 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; break;
} }
} }
if (!bias_calculated) { if (!bias_calculated) for (int i = 0; i < img->header->e_phnum; ++i) {
for (int i = 0; i < img->header->e_phnum; ++i) { if (phdr[i].p_type != PT_LOAD) continue;
if (phdr[i].p_type == PT_LOAD) {
img->bias = phdr[i].p_vaddr - phdr[i].p_offset;
bias_calculated = true;
LOGI("Calculated bias %ld from first PT_LOAD segment %d (vaddr %lx, offset %lx)", img->bias = phdr[i].p_vaddr - phdr[i].p_offset;
(long)img->bias, i, (unsigned long)phdr[i].p_vaddr, (unsigned long)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) { break;
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;
}
} }
} }
@@ -523,7 +503,7 @@ bool _load_symtabs(ElfImg *img) {
return true; 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) 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; 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)); ((uintptr_t)1 << ((hash >> img->gnu_shift2_) % bloom_mask_bits));
if ((mask & bloom_word) != mask) { if ((mask & bloom_word) != mask) {
LOGE("Symbol '%s' (hash %u) filtered out by GNU Bloom Filter (idx %zu, mask 0x%lx, word 0x%lx)", /* INFO: Very loggy -- generates too much noise. GNU is rarely used for Zygisk context. */
name, hash, bloom_idx, (unsigned long)mask, (unsigned long)bloom_word); /* 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; return 0;
} }
uint32_t sym_index = img->gnu_bucket_[hash % img->gnu_nbucket_]; uint32_t sym_index = img->gnu_bucket_[hash % img->gnu_nbucket_];
if (sym_index < img->gnu_symndx_) { 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; return 0;
} }
@@ -566,8 +548,12 @@ ElfW(Addr) GnuLookup(ElfImg *restrict img, const char *name, uint32_t hash) {
return 0; 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; return sym->st_value;
}
while ((chain_val & 1) == 0) { while ((chain_val & 1) == 0) {
sym_index++; sym_index++;
@@ -587,14 +573,18 @@ ElfW(Addr) GnuLookup(ElfImg *restrict img, const char *name, uint32_t hash) {
break; 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 sym->st_value;
}
} }
return 0; 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) if (img->nbucket_ == 0 || !img->bucket_ || !img->chain_ || !img->dynsym_start || !img->strtab_start)
return 0; 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]) { for (size_t n = img->bucket_[hash % img->nbucket_]; n != STN_UNDEF; n = img->chain_[n]) {
ElfW(Sym) *sym = img->dynsym_start + 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 sym->st_value;
}
} }
return 0; 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)) { if (!_load_symtabs(img)) {
LOGE("Failed to load symtabs for linear lookup of %s", name); 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) { if (valid_symtabs_amount == 0) {
LOGW("No valid symbols (FUNC/OBJECT with size > 0) found in .symtab for %s", img->elf); 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++) { 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) if (img->symtabs_[i].sym->st_shndx == SHN_UNDEF)
continue; 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 img->symtabs_[i].sym->st_value;
} }
return 0; 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)) { if (!_load_symtabs(img)) {
LOGE("Failed to load symtabs for linear lookup by prefix of %s", prefix); 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) { if (valid_symtabs_amount == 0) {
LOGW("No valid symbols (FUNC/OBJECT with size > 0) found in .symtab for %s", img->elf); 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); 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) if (img->symtabs_[i].sym->st_shndx == SHN_UNDEF)
continue; 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 img->symtabs_[i].sym->st_value;
} }
return 0; 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; ElfW(Addr) offset = 0;
offset = GnuLookup(img, name, GnuHash(name)); offset = GnuLookup(img, name, GnuHash(name), sym_type);
if (offset != 0) return offset; if (offset != 0) return offset;
offset = ElfLookup(img, name, ElfHash(name)); offset = ElfLookup(img, name, ElfHash(name), sym_type);
if (offset != 0) return offset; if (offset != 0) return offset;
offset = LinearLookup(img, name); offset = LinearLookup(img, name, sym_type);
if (offset != 0) return offset; if (offset != 0) return offset;
return 0; 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) 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; 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) 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; 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) { void *getSymbValueByPrefix(ElfImg *img, const char *prefix) {

View File

@@ -8,6 +8,50 @@
#include "socket_utils.h" #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 */ /* TODO: Standardize how to log errors */
int read_fd(int fd) { int read_fd(int fd) {
char cmsgbuf[CMSG_SPACE(sizeof(int))]; char cmsgbuf[CMSG_SPACE(sizeof(int))];
@@ -25,7 +69,7 @@ int read_fd(int fd) {
.msg_controllen = sizeof(cmsgbuf) .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) { if (ret == -1) {
PLOGE("recvmsg"); PLOGE("recvmsg");
@@ -47,14 +91,14 @@ int read_fd(int fd) {
ssize_t write_string(int fd, const char *str) { ssize_t write_string(int fd, const char *str) {
size_t str_len = strlen(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)) { 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)); LOGE("Failed to write string length: Not all bytes were written (%zd != %zu).\n", write_bytes, sizeof(size_t));
return -1; return -1;
} }
write_bytes = write(fd, str, str_len); write_bytes = write_loop(fd, str, str_len);
if (write_bytes != (ssize_t)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); 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) { char *read_string(int fd) {
size_t str_len = 0; 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)) { 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)); 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; return NULL;
} }
read_bytes = read(fd, buf, str_len); read_bytes = read_loop(fd, buf, str_len);
if (read_bytes != (ssize_t)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); 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; return buf;
} }
#define write_func(type) \ #define write_func(type) \
ssize_t write_## type(int fd, type val) { \ ssize_t write_## type(int fd, type val) { \
return write(fd, &val, sizeof(type)); \ return write_loop(fd, &val, sizeof(type)); \
} }
#define read_func(type) \ #define read_func(type) \
ssize_t read_## type(int fd, type *val) { \ ssize_t read_## type(int fd, type *val) { \
return read(fd, val, sizeof(type)); \ return read_loop(fd, val, sizeof(type)); \
} }
write_func(uint8_t) write_func(uint8_t)

View File

@@ -2,7 +2,3 @@ project(external)
OPTION(LSPLT_BUILD_SHARED OFF) OPTION(LSPLT_BUILD_SHARED OFF)
add_subdirectory(lsplt/lsplt/src/main/jni) 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)

View File

@@ -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"

View File

@@ -42,7 +42,7 @@ enum root_impl {
}; };
struct rezygisk_info { struct rezygisk_info {
struct zygisk_modules *modules; struct zygisk_modules modules;
enum root_impl root_impl; enum root_impl root_impl;
pid_t pid; pid_t pid;
bool running; bool running;

View File

@@ -1,13 +1,20 @@
#ifndef ELF_UTIL_H #ifndef ELF_UTIL_H
#define ELF_UTIL_H #define ELF_UTIL_H
#include <stdbool.h>
#include <string.h> #include <string.h>
#include <link.h> #include <link.h>
#include <linux/elf.h>
#include <sys/types.h> #include <sys/types.h>
#include <pthread.h> // Added for threading primitives
#define SHT_GNU_HASH 0x6ffffff6 #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 { struct symtabs {
char *name; char *name;
ElfW(Sym) *sym; ElfW(Sym) *sym;
@@ -54,17 +61,7 @@ void ElfImg_destroy(ElfImg *img);
ElfImg *ElfImg_create(const char *elf, void *base); ElfImg *ElfImg_create(const char *elf, void *base);
ElfW(Addr) ElfLookup(ElfImg *restrict img, const char *restrict name, uint32_t hash); ElfW(Addr) getSymbOffset(ElfImg *img, const char *name, unsigned char *sym_type);
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) getSymbAddress(ElfImg *img, const char *name); ElfW(Addr) getSymbAddress(ElfImg *img, const char *name);

View File

@@ -1,9 +1,13 @@
#ifndef MISC_H #ifndef MISC_H
#define MISC_H
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif /* __cplusplus */ #endif /* __cplusplus */
#define IS_ISOLATED_SERVICE(uid) \
((uid) >= 90000 && (uid) < 1000000)
/* /*
* Bionic's atoi runs through strtol(). * Bionic's atoi runs through strtol().
* Use our own implementation for faster conversion. * Use our own implementation for faster conversion.

View File

@@ -3,6 +3,10 @@
#include <stdint.h> #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); int read_fd(int fd);
ssize_t write_string(int fd, const char *str); ssize_t write_string(int fd, const char *str);

View 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 */

View File

@@ -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

View 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]);
}

View File

@@ -1,5 +1,6 @@
#include "daemon.h" #include "daemon.h"
#include "logging.h" #include "logging.h"
#include "solist.h"
#include "zygisk.hpp" #include "zygisk.hpp"
using namespace std; using namespace std;
@@ -8,7 +9,7 @@ void *start_addr = nullptr;
size_t block_size = 0; size_t block_size = 0;
extern "C" [[gnu::visibility("default")]] 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); LOGD("Zygisk library injected, version %s", ZKSU_VERSION);
start_addr = addr; start_addr = addr;
@@ -22,5 +23,11 @@ void entry(void* addr, size_t size, const char* path) {
LOGD("start plt hooking"); LOGD("start plt hooking");
hook_functions(); 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);
} }

View File

@@ -88,7 +88,7 @@ class ForkAndSpec(JNIHook):
return 'nativeForkAndSpecialize' return 'nativeForkAndSpecialize'
def init_args(self): 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): def body(self):
decl = '' decl = ''
@@ -117,7 +117,7 @@ class ForkServer(ForkAndSpec):
return 'nativeForkSystemServer' return 'nativeForkSystemServer'
def init_args(self): 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 # Common args
uid = Argument('uid', jint) uid = Argument('uid', jint)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,7 @@ namespace {
void *nativeForkAndSpecialize_orig = nullptr; 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) { [[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); ZygiskContext ctx(env, &args);
ctx.nativeForkAndSpecialize_pre(); ctx.nativeForkAndSpecialize_pre();
reinterpret_cast<decltype(&nativeForkAndSpecialize_l)>(nativeForkAndSpecialize_orig)( reinterpret_cast<decltype(&nativeForkAndSpecialize_l)>(nativeForkAndSpecialize_orig)(
@@ -14,7 +14,7 @@ void *nativeForkAndSpecialize_orig = nullptr;
return ctx.pid; 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) { [[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; args.fds_to_ignore = &fds_to_ignore;
ZygiskContext ctx(env, &args); ZygiskContext ctx(env, &args);
ctx.nativeForkAndSpecialize_pre(); ctx.nativeForkAndSpecialize_pre();
@@ -25,7 +25,7 @@ void *nativeForkAndSpecialize_orig = nullptr;
return ctx.pid; 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) { [[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.fds_to_ignore = &fds_to_ignore;
args.is_child_zygote = &is_child_zygote; args.is_child_zygote = &is_child_zygote;
ZygiskContext ctx(env, &args); ZygiskContext ctx(env, &args);
@@ -37,7 +37,7 @@ void *nativeForkAndSpecialize_orig = nullptr;
return ctx.pid; 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) { [[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.fds_to_ignore = &fds_to_ignore;
args.is_child_zygote = &is_child_zygote; args.is_child_zygote = &is_child_zygote;
args.is_top_app = &is_top_app; args.is_top_app = &is_top_app;
@@ -50,7 +50,7 @@ void *nativeForkAndSpecialize_orig = nullptr;
return ctx.pid; 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) { [[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.fds_to_ignore = &fds_to_ignore;
args.is_child_zygote = &is_child_zygote; args.is_child_zygote = &is_child_zygote;
args.is_top_app = &is_top_app; args.is_top_app = &is_top_app;
@@ -67,7 +67,7 @@ void *nativeForkAndSpecialize_orig = nullptr;
return ctx.pid; 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) { [[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.fds_to_ignore = &fds_to_ignore;
args.is_child_zygote = &is_child_zygote; args.is_child_zygote = &is_child_zygote;
args.is_top_app = &is_top_app; args.is_top_app = &is_top_app;
@@ -85,7 +85,7 @@ void *nativeForkAndSpecialize_orig = nullptr;
return ctx.pid; 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) { [[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); ZygiskContext ctx(env, &args);
ctx.nativeForkAndSpecialize_pre(); ctx.nativeForkAndSpecialize_pre();
reinterpret_cast<decltype(&nativeForkAndSpecialize_samsung_m)>(nativeForkAndSpecialize_orig)( reinterpret_cast<decltype(&nativeForkAndSpecialize_samsung_m)>(nativeForkAndSpecialize_orig)(
@@ -95,7 +95,7 @@ void *nativeForkAndSpecialize_orig = nullptr;
return ctx.pid; 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) { [[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); ZygiskContext ctx(env, &args);
ctx.nativeForkAndSpecialize_pre(); ctx.nativeForkAndSpecialize_pre();
reinterpret_cast<decltype(&nativeForkAndSpecialize_samsung_n)>(nativeForkAndSpecialize_orig)( reinterpret_cast<decltype(&nativeForkAndSpecialize_samsung_n)>(nativeForkAndSpecialize_orig)(
@@ -105,7 +105,7 @@ void *nativeForkAndSpecialize_orig = nullptr;
return ctx.pid; 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) { [[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; args.fds_to_ignore = &fds_to_ignore;
ZygiskContext ctx(env, &args); ZygiskContext ctx(env, &args);
ctx.nativeForkAndSpecialize_pre(); ctx.nativeForkAndSpecialize_pre();
@@ -116,7 +116,7 @@ void *nativeForkAndSpecialize_orig = nullptr;
return ctx.pid; 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) { [[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.fds_to_ignore = &fds_to_ignore;
args.is_child_zygote = &is_child_zygote; args.is_child_zygote = &is_child_zygote;
ZygiskContext ctx(env, &args); ZygiskContext ctx(env, &args);
@@ -128,7 +128,7 @@ void *nativeForkAndSpecialize_orig = nullptr;
return ctx.pid; 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) { [[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.fds_to_ignore = &fds_to_ignore;
args.is_child_zygote = &is_child_zygote; args.is_child_zygote = &is_child_zygote;
args.is_top_app = &is_top_app; args.is_top_app = &is_top_app;
@@ -205,7 +205,7 @@ std::array nativeForkAndSpecialize_methods = {
void *nativeSpecializeAppProcess_orig = nullptr; 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) { [[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; args.is_child_zygote = &is_child_zygote;
ZygiskContext ctx(env, &args); ZygiskContext ctx(env, &args);
ctx.nativeSpecializeAppProcess_pre(); ctx.nativeSpecializeAppProcess_pre();
@@ -215,7 +215,7 @@ void *nativeSpecializeAppProcess_orig = nullptr;
ctx.nativeSpecializeAppProcess_post(); 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) { [[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_child_zygote = &is_child_zygote;
args.is_top_app = &is_top_app; args.is_top_app = &is_top_app;
ZygiskContext ctx(env, &args); ZygiskContext ctx(env, &args);
@@ -226,7 +226,7 @@ void *nativeSpecializeAppProcess_orig = nullptr;
ctx.nativeSpecializeAppProcess_post(); 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) { [[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_child_zygote = &is_child_zygote;
args.is_top_app = &is_top_app; args.is_top_app = &is_top_app;
args.pkg_data_info_list = &pkg_data_info_list; args.pkg_data_info_list = &pkg_data_info_list;
@@ -241,7 +241,7 @@ void *nativeSpecializeAppProcess_orig = nullptr;
ctx.nativeSpecializeAppProcess_post(); 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) { [[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_child_zygote = &is_child_zygote;
args.is_top_app = &is_top_app; args.is_top_app = &is_top_app;
args.pkg_data_info_list = &pkg_data_info_list; args.pkg_data_info_list = &pkg_data_info_list;
@@ -257,7 +257,7 @@ void *nativeSpecializeAppProcess_orig = nullptr;
ctx.nativeSpecializeAppProcess_post(); 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) { [[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; args.is_child_zygote = &is_child_zygote;
ZygiskContext ctx(env, &args); ZygiskContext ctx(env, &args);
ctx.nativeSpecializeAppProcess_pre(); ctx.nativeSpecializeAppProcess_pre();
@@ -267,7 +267,7 @@ void *nativeSpecializeAppProcess_orig = nullptr;
ctx.nativeSpecializeAppProcess_post(); 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) { [[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_child_zygote = &is_child_zygote;
args.is_top_app = &is_top_app; args.is_top_app = &is_top_app;
args.pkg_data_info_list = &pkg_data_info_list; args.pkg_data_info_list = &pkg_data_info_list;
@@ -317,7 +317,7 @@ std::array nativeSpecializeAppProcess_methods = {
void *nativeForkSystemServer_orig = nullptr; 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) { [[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); ZygiskContext ctx(env, &args);
ctx.nativeForkSystemServer_pre(); ctx.nativeForkSystemServer_pre();
reinterpret_cast<decltype(&nativeForkSystemServer_l)>(nativeForkSystemServer_orig)( reinterpret_cast<decltype(&nativeForkSystemServer_l)>(nativeForkSystemServer_orig)(
@@ -327,7 +327,7 @@ void *nativeForkSystemServer_orig = nullptr;
return ctx.pid; 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) { [[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); ZygiskContext ctx(env, &args);
ctx.nativeForkSystemServer_pre(); ctx.nativeForkSystemServer_pre();
reinterpret_cast<decltype(&nativeForkSystemServer_samsung_q)>(nativeForkSystemServer_orig)( reinterpret_cast<decltype(&nativeForkSystemServer_samsung_q)>(nativeForkSystemServer_orig)(
@@ -337,7 +337,7 @@ void *nativeForkSystemServer_orig = nullptr;
return ctx.pid; 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) { [[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); ZygiskContext ctx(env, &args);
ctx.nativeForkSystemServer_pre(); ctx.nativeForkSystemServer_pre();
reinterpret_cast<decltype(&nativeForkSystemServer_grapheneos_u)>(nativeForkSystemServer_orig)( reinterpret_cast<decltype(&nativeForkSystemServer_grapheneos_u)>(nativeForkSystemServer_orig)(

View 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 */

View File

@@ -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

View File

@@ -1,28 +1,30 @@
#include <stdio.h> #include <stdio.h>
#include <stdbool.h> #include <stdbool.h>
#include <string.h> #include <string.h>
#include <dlfcn.h>
#include <linux/limits.h>
#include "elf_util.h" #include "elf_util.h"
#include "logging.h" #include "logging.h"
#include "solist.h" #include "solist.h"
/* TODO: Is offset for realpath necessary? It seems to have the function
available anywhere. */
#ifdef __LP64__ #ifdef __LP64__
size_t solist_size_offset = 0x18; size_t solist_size_offset = 0x18;
size_t solist_next_offset = 0x30;
size_t solist_realpath_offset = 0x1a8; size_t solist_realpath_offset = 0x1a8;
#else #else
size_t solist_size_offset = 0x90; size_t solist_size_offset = 0x90;
size_t solist_next_offset = 0xa4;
size_t solist_realpath_offset = 0x174; size_t solist_realpath_offset = 0x174;
#endif #endif
static const char *(*get_realpath_sym)(SoInfo *) = NULL; static const char *(*get_realpath_sym)(SoInfo *) = NULL;
static void (*soinfo_free)(SoInfo *) = NULL; static void (*soinfo_free)(SoInfo *) = NULL;
static SoInfo *(*find_containing_library)(const void *p) = NULL;
static inline SoInfo *get_next(SoInfo *self) { static void (*purge_unused_memory)(void) = NULL;
return *(SoInfo **)((uintptr_t)self + solist_next_offset); struct link_map *r_debug_tail = NULL;
}
static inline const char *get_path(SoInfo *self) { static inline const char *get_path(SoInfo *self) {
if (get_realpath_sym) 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; *(size_t *) ((uintptr_t)self + solist_size_offset) = size;
} }
static inline size_t get_size(SoInfo *self) { struct pdg ppdg = { 0 };
return *(size_t *) ((uintptr_t)self + solist_size_offset);
}
struct pdg ppdg;
static bool pdg_setup(ElfImg *img) { static bool pdg_setup(ElfImg *img) {
ppdg.ctor = (void *(*)())getSymbAddress(img, "__dl__ZN18ProtectedDataGuardC2Ev"); ppdg.ctor = (void *(*)())getSymbAddress(img, "__dl__ZN18ProtectedDataGuardC2Ev");
@@ -48,22 +46,20 @@ static bool pdg_setup(ElfImg *img) {
return ppdg.ctor != NULL && ppdg.dtor != NULL; return ppdg.ctor != NULL && ppdg.dtor != NULL;
} }
static void pdg_protect() { /* INFO: Allow data to be written to the areas. */
if (ppdg.ctor != NULL)
(*(ppdg.ctor))();
}
static void pdg_unprotect() { static void pdg_unprotect() {
if (ppdg.dtor != NULL) (*ppdg.ctor)();
(*(ppdg.dtor))();
} }
static SoInfo *solist = NULL; /* INFO: Block write and only allow read access to the areas. */
static SoInfo *somain = NULL; static void pdg_protect() {
static SoInfo **sonext = NULL; (*ppdg.dtor)();
}
static uint64_t *g_module_load_counter = NULL; static SoInfo *somain = NULL;
static uint64_t *g_module_unload_counter = NULL;
static size_t *g_module_load_counter = NULL;
static size_t *g_module_unload_counter = NULL;
static bool solist_init() { static bool solist_init() {
#ifdef __LP64__ #ifdef __LP64__
@@ -77,10 +73,6 @@ static bool solist_init() {
return false; return false;
} }
ppdg = (struct pdg) {
.ctor = NULL,
.dtor = NULL
};
if (!pdg_setup(linker)) { if (!pdg_setup(linker)) {
LOGE("Failed to setup pdg"); LOGE("Failed to setup pdg");
@@ -96,17 +88,6 @@ static bool solist_init() {
See #63 for more information. 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"); somain = (SoInfo *)getSymbValueByPrefix(linker, "__dl__ZL6somain");
if (somain == NULL) { if (somain == NULL) {
LOGE("Failed to find somain __dl__ZL6somain*"); LOGE("Failed to find somain __dl__ZL6somain*");
@@ -118,26 +99,14 @@ static bool solist_init() {
LOGD("%p is somain", (void *)somain); 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"); get_realpath_sym = (const char *(*)(SoInfo *))getSymbAddress(linker, "__dl__ZNK6soinfo12get_realpathEv");
if (get_realpath_sym == NULL) { if (get_realpath_sym == NULL) {
LOGE("Failed to find get_realpath __dl__ZNK6soinfo12get_realpathEv"); LOGE("Failed to find get_realpath __dl__ZNK6soinfo12get_realpathEv");
ElfImg_destroy(linker); ElfImg_destroy(linker);
somain = NULL;
return false; return false;
} }
@@ -149,30 +118,63 @@ static bool solist_init() {
ElfImg_destroy(linker); ElfImg_destroy(linker);
somain = NULL;
return false; return false;
} }
LOGD("%p is soinfo_free", (void *)soinfo_free); 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"); 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"); if (g_module_unload_counter != NULL) LOGD("found symbol g_module_unload_counter");
for (size_t i = 0; i < 1024 / sizeof(void *); i++) { 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 *)); 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) { if (possible_size_of_somain < 0x100000 && possible_size_of_somain > 0x100) {
solist_size_offset = i * sizeof(void *); solist_size_offset = i * sizeof(void *);
LOGD("solist_size_offset is %zu * %zu = %p", i, sizeof(void *), (void *)solist_size_offset); 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; break;
} }
@@ -183,36 +185,144 @@ static bool solist_init() {
return true; return true;
} }
bool solist_drop_so_path(const char *target_path) { /* INFO: This is an AOSP function to remove a link map from
if (solist == NULL && !solist_init()) { 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"); LOGE("Failed to initialize solist");
return false; return false;
} }
for (SoInfo *iter = solist; iter; iter = get_next(iter)) { SoInfo *found = (*find_containing_library)(lib_memory);
if (get_path(iter) && strstr(get_path(iter), target_path)) { if (found == NULL) {
pdg_protect(); 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)); purge_unused_memory();
if (get_size(iter) > 0) {
set_size(iter, 0);
soinfo_free(iter);
pdg_unprotect(); return false;
return true;
}
pdg_unprotect();
}
} }
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) { 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"); LOGE("Failed to initialize solist");
return; return;
@@ -224,18 +334,18 @@ void solist_reset_counters(size_t load, size_t unload) {
return; return;
} }
uint64_t loaded_modules = *g_module_load_counter; size_t loaded_modules = *g_module_load_counter;
uint64_t unloaded_modules = *g_module_unload_counter; size_t unloaded_modules = *g_module_unload_counter;
if (loaded_modules >= load) { 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) { 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);
} }
} }

View File

@@ -5,11 +5,7 @@
extern "C" { extern "C" {
#endif /* __cplusplus */ #endif /* __cplusplus */
typedef struct SoInfo SoInfo; typedef void SoInfo;
struct SoInfo {
char data[0];
};
#define FuncType(name) void (*name) #define FuncType(name) void (*name)
@@ -28,14 +24,20 @@ struct pdg {
libzygisk.so, so that it doesn't create gaps between current module info libzygisk.so, so that it doesn't create gaps between current module info
and the next (soinfo). and the next (soinfo).
To do that, we use 2 functions: soinfo_free, and set_size, which will To do that, we use 2 functions: set_size and dlclose, which will first zero
zero the region size, and then remove all traces of that library (libzygisk.so) zero the size that the linker believes the shared library is, and then dlclose.
which was previously loaded. 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: SOURCES:
- https://android.googlesource.com/platform/bionic/+/refs/heads/android15-release/linker/linker.cpp#1712 - 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 INFO: When dlopen'ing a library, the system will increment 1 to a global

View File

@@ -7,4 +7,4 @@ extern size_t block_size;
void hook_functions(); 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();

View File

@@ -77,11 +77,11 @@ int main(int argc, char **argv) {
} }
} }
if (info.modules->modules_count != 0) { if (info.modules.modules_count != 0) {
printf("Modules: %zu\n", info.modules->modules_count); printf("Modules: %zu\n", info.modules.modules_count);
for (size_t i = 0; i < info.modules->modules_count; i++) { for (size_t i = 0; i < info.modules.modules_count; i++) {
printf(" - %s\n", info.modules->modules[i]); printf(" - %s\n", info.modules.modules[i]);
} }
} else { } else {
printf("Modules: N/A\n"); printf("Modules: N/A\n");

View File

@@ -17,6 +17,7 @@
#include "utils.h" #include "utils.h"
#include "daemon.h" #include "daemon.h"
#include "misc.h" #include "misc.h"
#include "socket_utils.h"
#include "monitor.h" #include "monitor.h"
@@ -29,6 +30,15 @@ static bool update_status(const char *message);
char monitor_stop_reason[32]; 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 { enum ptracer_tracing_state {
TRACING, TRACING,
STOPPING, STOPPING,
@@ -66,6 +76,7 @@ struct rezygiskd_status status32 = {
int monitor_epoll_fd; int monitor_epoll_fd;
bool monitor_events_running = true; bool monitor_events_running = true;
typedef void (*monitor_event_callback_t)();
bool monitor_events_init() { bool monitor_events_init() {
monitor_epoll_fd = epoll_create(1); monitor_epoll_fd = epoll_create(1);
@@ -78,14 +89,9 @@ bool monitor_events_init() {
return true; return true;
} }
struct monitor_event_cbs { bool monitor_events_register_event(monitor_event_callback_t event_cb, int fd, uint32_t events) {
void (*callback)();
void (*stop_callback)();
};
bool monitor_events_register_event(struct monitor_event_cbs *event_cbs, int fd, uint32_t events) {
struct epoll_event ev = { struct epoll_event ev = {
.data.ptr = event_cbs, .data.ptr = (void *)event_cb,
.events = events .events = events
}; };
@@ -116,15 +122,16 @@ void monitor_events_loop() {
struct epoll_event events[2]; struct epoll_event events[2];
while (monitor_events_running) { while (monitor_events_running) {
int nfds = epoll_wait(monitor_epoll_fd, events, 2, -1); int nfds = epoll_wait(monitor_epoll_fd, events, 2, -1);
if (nfds == -1) { if (nfds == -1 && errno != EINTR) {
if (errno != EINTR) PLOGE("epoll_wait"); PLOGE("epoll_wait");
continue; monitor_events_running = false;
break;
} }
for (int i = 0; i < nfds; i++) { for (int i = 0; i < nfds; i++) {
struct monitor_event_cbs *event_cbs = (struct monitor_event_cbs *)events[i].data.ptr; ((monitor_event_callback_t)events[i].data.ptr)();
event_cbs->callback();
if (!monitor_events_running) break; if (!monitor_events_running) break;
} }
@@ -132,11 +139,6 @@ void monitor_events_loop() {
if (monitor_epoll_fd >= 0) close(monitor_epoll_fd); if (monitor_epoll_fd >= 0) close(monitor_epoll_fd);
monitor_epoll_fd = -1; 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; int monitor_sock_fd;
@@ -166,57 +168,26 @@ bool rezygiskd_listener_init() {
return true; return true;
} }
struct __attribute__((__packed__)) MsgHead {
unsigned int cmd;
int length;
};
void rezygiskd_listener_callback() { void rezygiskd_listener_callback() {
while (1) { 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; continue;
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;
}
} }
switch (msg.cmd) { switch (cmd) {
case START: { case START: {
if (tracing_state == STOPPING) tracing_state = TRACING; if (tracing_state == STOPPING) {
else if (tracing_state == STOPPED) { LOGI("Continue tracing init");
ptrace(PTRACE_SEIZE, 1, 0, PTRACE_O_TRACEFORK);
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; tracing_state = TRACING;
} }
@@ -227,7 +198,7 @@ void rezygiskd_listener_callback() {
} }
case STOP: { case STOP: {
if (tracing_state == TRACING) { if (tracing_state == TRACING) {
LOGI("stop tracing requested"); LOGI("Stop tracing requested");
tracing_state = STOPPING; tracing_state = STOPPING;
strcpy(monitor_stop_reason, "user requested"); strcpy(monitor_stop_reason, "user requested");
@@ -239,7 +210,7 @@ void rezygiskd_listener_callback() {
break; break;
} }
case EXIT: { case EXIT: {
LOGI("prepare for exit ..."); LOGI("Prepare for exit ...");
tracing_state = EXITING; tracing_state = EXITING;
strcpy(monitor_stop_reason, "user requested"); strcpy(monitor_stop_reason, "user requested");
@@ -249,104 +220,166 @@ void rezygiskd_listener_callback() {
break; break;
} }
case ZYGOTE64_INJECTED: { case ZYGOTE64_INJECTED:
status64.zygote_injected = true;
update_status(NULL);
break;
}
case ZYGOTE32_INJECTED: { case ZYGOTE32_INJECTED: {
status32.zygote_injected = true; LOGI("Received Zygote%s injected command", cmd == ZYGOTE64_INJECTED ? "64" : "32");
update_status(NULL); struct rezygiskd_status *status = cmd == ZYGOTE64_INJECTED ? &status64 : &status32;
status->zygote_injected = true;
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);
update_status(NULL); update_status(NULL);
break; break;
} }
case DAEMON64_SET_INFO:
case DAEMON32_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) { uint32_t root_impl_len;
free(status32.daemon_info); if (read_uint32_t(monitor_sock_fd, &root_impl_len) != sizeof(root_impl_len)) {
status32.daemon_info = NULL; LOGE("read ReZygiskd%s root impl len", cmd == DAEMON64_SET_INFO ? "64" : "32");
}
status32.daemon_info = (char *)malloc(msg.length);
if (!status32.daemon_info) {
PLOGE("malloc daemon32 info");
break; break;
} }
strcpy(status32.daemon_info, msg_data); struct environment_information *environment_information = cmd == DAEMON64_SET_INFO ? &environment_information64 : &environment_information32;
if (environment_information->root_impl) {
update_status(NULL); LOGD("freeing old ReZygiskd%s root impl", cmd == DAEMON64_SET_INFO ? "64" : "32");
break; free((void *)environment_information->root_impl);
} environment_information->root_impl = NULL;
case DAEMON64_SET_ERROR_INFO: { }
LOGD("received daemon64 error info %s", msg_data);
environment_information->root_impl = malloc(root_impl_len + 1);
status64.daemon_running = false; if (environment_information->root_impl == NULL) {
PLOGE("malloc ReZygiskd%s root impl", cmd == DAEMON64_SET_INFO ? "64" : "32");
if (status64.daemon_error_info) {
free(status64.daemon_error_info); break;
status64.daemon_error_info = NULL; }
}
if (read_loop(monitor_sock_fd, (void *)environment_information->root_impl, root_impl_len) != (ssize_t)root_impl_len) {
status64.daemon_error_info = (char *)malloc(msg.length); LOGE("read ReZygiskd%s root impl", cmd == DAEMON64_SET_INFO ? "64" : "32");
if (!status64.daemon_error_info) {
PLOGE("malloc daemon64 error info"); free((void *)environment_information->root_impl);
environment_information->root_impl = NULL;
break;
} break;
}
strcpy(status64.daemon_error_info, msg_data);
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); update_status(NULL);
break; break;
} }
case DAEMON64_SET_ERROR_INFO:
case DAEMON32_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; uint32_t error_info_len;
if (read_uint32_t(monitor_sock_fd, &error_info_len) != sizeof(error_info_len)) {
if (status32.daemon_error_info) { LOGE("read ReZygiskd%s error info len", cmd == DAEMON64_SET_ERROR_INFO ? "64" : "32");
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");
break; 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); update_status(NULL);
@@ -363,8 +396,6 @@ void rezygiskd_listener_callback() {
} }
} }
if (msg_data) free(msg_data);
break; break;
} }
} }
@@ -439,51 +470,49 @@ static bool ensure_daemon_created(bool is_64bit) {
return true; return true;
} }
#define CHECK_DAEMON_EXIT(abi) \ #define CHECK_DAEMON_EXIT(abi) \
if (status##abi.supported && pid == status##abi.daemon_pid) { \ if (status##abi.supported && pid == status##abi.daemon_pid) { \
char status_str[64]; \ char status_str[64]; \
parse_status(sigchld_status, status_str, sizeof(status_str)); \ parse_status(sigchld_status, status_str, sizeof(status_str)); \
\ \
LOGW("daemon" #abi " pid %d exited: %s", pid, status_str); \ LOGW("daemon" #abi " pid %d exited: %s", pid, status_str); \
status##abi.daemon_running = false; \ status##abi.daemon_running = false; \
\ \
if (!status##abi.daemon_error_info) { \ if (!status##abi.daemon_error_info) { \
status##abi.daemon_error_info = (char *)malloc(strlen(status_str) + 1); \ status##abi.daemon_error_info = strdup(status_str); \
if (!status##abi.daemon_error_info) { \ if (!status##abi.daemon_error_info) { \
LOGE("malloc daemon" #abi " error info failed"); \ LOGE("malloc daemon" #abi " error info failed"); \
\ \
return; \ return; \
} \ } \
\ } \
memcpy(status##abi.daemon_error_info, status_str, strlen(status_str) + 1); \ \
} \ continue; \
\
update_status(NULL); \
continue; \
} }
#define PRE_INJECT(abi, is_64) \ #define PRE_INJECT(abi, is_64) \
if (strcmp(program, "/system/bin/app_process" # abi) == 0) { \ if (strcmp(program, "/system/bin/app_process" # abi) == 0) { \
tracer = "./bin/zygisk-ptrace" # abi; \ tracer = "./bin/zygisk-ptrace" # abi; \
\ \
if (should_stop_inject ## abi()) { \ if (should_stop_inject ## abi()) { \
LOGW("zygote" # abi " restart too much times, stop injecting"); \ LOGW("Zygote" # abi " restart too much times, stop injecting"); \
\ \
tracing_state = STOPPING; \ tracing_state = STOPPING; \
memcpy(monitor_stop_reason, "zygote crashed", sizeof("zygote crashed")); \ strcpy(monitor_stop_reason, "Zygote crashed"); \
ptrace(PTRACE_INTERRUPT, 1, 0, 0); \ ptrace(PTRACE_INTERRUPT, 1, 0, 0); \
\ \
break; \ break; \
} \ } \
if (!ensure_daemon_created(is_64)) { \ \
LOGW("daemon" #abi " not running, stop injecting"); \ if (!ensure_daemon_created(is_64)) { \
\ LOGW("ReZygiskd " #abi "-bit not running, stop injecting"); \
tracing_state = STOPPING; \ \
memcpy(monitor_stop_reason, "daemon not running", sizeof("daemon not running")); \ tracing_state = STOPPING; \
ptrace(PTRACE_INTERRUPT, 1, 0, 0); \ strcpy(monitor_stop_reason, "ReZygiskd not running"); \
\ ptrace(PTRACE_INTERRUPT, 1, 0, 0); \
break; \ \
} \ break; \
} \
} }
int sigchld_signal_fd; int sigchld_signal_fd;
@@ -677,8 +706,6 @@ void sigchld_listener_callback() {
} }
} }
} while (false); } while (false);
update_status(NULL);
} else { } else {
char status_str[64]; char status_str[64];
parse_status(sigchld_status, status_str, sizeof(status_str)); parse_status(sigchld_status, status_str, sizeof(status_str));
@@ -718,27 +745,20 @@ static char post_section[1024];
#define WRITE_STATUS_ABI(suffix) \ #define WRITE_STATUS_ABI(suffix) \
if (status ## suffix.supported) { \ if (status ## suffix.supported) { \
strcat(status_text, " zygote" # suffix ": "); \ strcat(status_text, ", ReZygisk " # suffix "-bit: "); \
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, "daemon" # suffix ": "); \ if (tracing_state != TRACING) strcat(status_text, "❌"); \
if (status ## suffix.daemon_running) { \ else if (status ## suffix.zygote_injected && status ## suffix.daemon_running) \
strcat(status_text, "😋 running "); \ strcat(status_text, "✅"); \
else strcat(status_text, "⚠️"); \
\ \
if (status ## suffix.daemon_info != NULL) { \ if (!status ## suffix.daemon_running) { \
strcat(status_text, "("); \ if (status ## suffix.daemon_error_info) { \
strcat(status_text, status ## suffix.daemon_info); \ strcat(status_text, "(ReZygiskd: "); \
strcat(status_text, ")"); \
} \
} else { \
strcat(status_text, "❌ crashed "); \
\
if (status ## suffix.daemon_error_info != NULL) { \
strcat(status_text, "("); \
strcat(status_text, status ## suffix.daemon_error_info); \ strcat(status_text, status ## suffix.daemon_error_info); \
strcat(status_text, ")"); \ strcat(status_text, ")"); \
} else { \
strcat(status_text, "(ReZygiskd: not running)"); \
} \ } \
} \ } \
} }
@@ -758,41 +778,106 @@ static bool update_status(const char *message) {
return true; return true;
} }
char status_text[1024] = "monitor: "; char status_text[256] = "Monitor: ";
switch (tracing_state) { switch (tracing_state) {
case TRACING: { case TRACING: {
strcat(status_text, "😋 tracing"); strcat(status_text, "");
break; break;
} }
case STOPPING: [[fallthrough]]; case STOPPING: [[fallthrough]];
case STOPPED: { case STOPPED: {
strcat(status_text, "❌ stopped"); strcat(status_text, "");
break; break;
} }
case EXITING: { case EXITING: {
strcat(status_text, " exited"); strcat(status_text, "");
break; 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(64)
WRITE_STATUS_ABI(32) WRITE_STATUS_ABI(32)
fprintf(prop, "%s[%s] %s", pre_section, status_text, post_section); fprintf(prop, "%s[%s] %s", pre_section, status_text, post_section);
fclose(prop); 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; return true;
} }
@@ -837,7 +922,7 @@ static bool prepare_environment() {
return false; return false;
} }
return update_status(NULL); return true;
} }
void init_monitor() { void init_monitor() {
@@ -847,10 +932,6 @@ void init_monitor() {
monitor_events_init(); monitor_events_init();
struct monitor_event_cbs listener_cbs = {
.callback = rezygiskd_listener_callback,
.stop_callback = rezygiskd_listener_stop
};
if (!rezygiskd_listener_init()) { if (!rezygiskd_listener_init()) {
LOGE("failed to create socket"); LOGE("failed to create socket");
@@ -859,12 +940,8 @@ void init_monitor() {
exit(1); 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) { if (sigchld_listener_init() == false) {
LOGE("failed to create signalfd"); LOGE("failed to create signalfd");
@@ -874,16 +951,37 @@ void init_monitor() {
exit(1); 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(); 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_info) free(status64.daemon_info);
if (status64.daemon_error_info) free(status64.daemon_error_info); if (status64.daemon_error_info) free(status64.daemon_error_info);
if (status32.daemon_info) free(status32.daemon_info); if (status32.daemon_info) free(status32.daemon_info);
if (status32.daemon_error_info) free(status32.daemon_error_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) { 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; 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); close(sockfd);
return nsend != sizeof(cmd) ? -1 : 0; return nsend != sizeof(cmd_op) ? -1 : 0;
} }

View File

@@ -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"); void *libc_return_addr = find_module_return_addr(map, "libc.so");
LOGD("libc return addr %p", libc_return_addr); 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 */ /* call dlopen */
#ifdef __LP64__ void *dlopen_addr = NULL;
void *dlopen_addr = find_func_addr(local_map, map, "/apex/com.android.runtime/lib64/bionic/libdl.so", "dlopen"); if (!libdl_path || (dlopen_addr = find_func_addr(local_map, map, libdl_path, "dlopen")) == NULL) {
#else
void *dlopen_addr = find_func_addr(local_map, map, "/apex/com.android.runtime/lib/bionic/libdl.so", "dlopen");
#endif
if (dlopen_addr == NULL) {
/* INFO: Android 7.1 and below doesn't have libdl.so loaded in Zygote */ /* 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"); 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) { if (dlopen_addr == NULL) {
PLOGE("Find __dl_dlopen"); PLOGE("Find __dl_dlopen");
@@ -198,16 +228,16 @@ bool inject_on_main(int pid, const char *lib_path) {
LOGE("handle is null"); LOGE("handle is null");
/* call dlerror */ /* call dlerror */
#ifdef __LP64__ void *dlerror_addr = NULL;
void *dlerror_addr = find_func_addr(local_map, map, "/apex/com.android.runtime/lib64/bionic/libdl.so", "dlerror"); if (!libdl_path || (dlerror_addr = find_func_addr(local_map, map, libdl_path, "dlerror")) == NULL) {
#else
void *dlerror_addr = find_func_addr(local_map, map, "/apex/com.android.runtime/lib/bionic/libdl.so", "dlerror");
#endif
if (dlerror_addr == NULL) {
/* INFO: Android 7.1 and below doesn't have libdl.so loaded in Zygote */ /* 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"); 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) { if (dlerror_addr == NULL) {
LOGE("Find __dl_dlerror"); LOGE("Find __dl_dlerror");
@@ -225,19 +255,19 @@ bool inject_on_main(int pid, const char *lib_path) {
LOGE("dlerror str is null"); LOGE("dlerror str is null");
free(args); free(args);
free_maps(local_map);
free_maps(map);
return false; return false;
} }
#ifdef __LP64__ void *strlen_addr = find_func_addr(local_map, map, libc_path, "strlen");
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
if (strlen_addr == NULL) { if (strlen_addr == NULL) {
LOGE("find strlen"); LOGE("find strlen");
free(args); free(args);
free_maps(local_map);
free_maps(map);
return false; return false;
} }
@@ -249,6 +279,8 @@ bool inject_on_main(int pid, const char *lib_path) {
LOGE("dlerror len <= 0"); LOGE("dlerror len <= 0");
free(args); free(args);
free_maps(local_map);
free_maps(map);
return false; return false;
} }
@@ -258,6 +290,8 @@ bool inject_on_main(int pid, const char *lib_path) {
LOGE("malloc err"); LOGE("malloc err");
free(args); free(args);
free_maps(local_map);
free_maps(map);
return false; return false;
} }
@@ -269,20 +303,23 @@ bool inject_on_main(int pid, const char *lib_path) {
free(err); free(err);
free(args); free(args);
free_maps(local_map);
free_maps(map);
return false; return false;
} }
/* call dlsym(handle, "entry") */ /* call dlsym(handle, "entry") */
#ifdef __LP64__ void *dlsym_addr = NULL;
void *dlsym_addr = find_func_addr(local_map, map, "/apex/com.android.runtime/lib64/bionic/libdl.so", "dlsym"); if (!libdl_path || (dlsym_addr = find_func_addr(local_map, map, libdl_path, "dlsym")) == NULL) {
#else
void *dlsym_addr = find_func_addr(local_map, map, "/apex/com.android.runtime/lib/bionic/libdl.so", "dlsym");
#endif
if (dlsym_addr == NULL) {
/* INFO: Android 7.1 and below doesn't have libdl.so loaded in Zygote */ /* 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"); 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) { if (dlsym_addr == NULL) {
LOGE("find __dl_dlsym"); 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) */ /* call injector entry(start_addr, block_size, path) */
args[0] = (uintptr_t)start_addr; args[0] = (uintptr_t)start_addr;
args[1] = block_size; args[1] = block_size;
str = push_string(pid, &regs, rezygiskd_get_path());
args[2] = (uintptr_t)str;
remote_call(pid, &regs, injector_entry, (uintptr_t)libc_return_addr, args, 3); remote_call(pid, &regs, injector_entry, (uintptr_t)libc_return_addr, args, 2);
free(args); free(args);
@@ -370,7 +405,7 @@ bool trace_zygote(int pid) {
int status; 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"); PLOGE("seize");
return false; return false;
@@ -405,6 +440,16 @@ bool trace_zygote(int pid) {
if (STOPPED_WITH(SIGCONT, 0)) { if (STOPPED_WITH(SIGCONT, 0)) {
LOGD("received SIGCONT"); 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); ptrace(PTRACE_DETACH, pid, 0, SIGCONT);
} }
} else { } else {

View File

@@ -119,8 +119,8 @@ struct maps *parse_maps(const char *filename) {
path_offset++; path_offset++;
} }
maps->maps = (struct map *)realloc(maps->maps, (i + 1) * sizeof(struct map)); struct map *tmp_maps = (struct map *)realloc(maps->maps, (i + 1) * sizeof(struct map));
if (!maps->maps) { if (!tmp_maps) {
LOGE("Failed to allocate memory for maps->maps"); LOGE("Failed to allocate memory for maps->maps");
maps->size = i; maps->size = i;
@@ -130,6 +130,7 @@ struct maps *parse_maps(const char *filename) {
return NULL; return NULL;
} }
maps->maps = tmp_maps;
maps->maps[i].start = addr_start; maps->maps[i].start = addr_start;
maps->maps[i].end = addr_end; 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) { 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++) { for (size_t i = 0; i < map->size; i++) {
if (map->maps[i].path == NULL) continue; if (map->maps[i].path == NULL) continue;
const char *file_name = position_after(map->maps[i].path, '/'); const char *file_path = map->maps[i].path;
if (!file_name) continue;
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; return (void *)map->maps[i].start;
} }
@@ -530,6 +523,32 @@ int fork_dont_care() {
return pid; return pid;
} }
void tracee_skip_syscall(int pid) {
struct user_regs_struct regs;
if (!get_regs(pid, &regs)) {
LOGE("failed to get seccomp regs");
exit(1);
}
regs.REG_SYSNR = -1;
if (!set_regs(pid, &regs)) {
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) { void wait_for_trace(int pid, int *status, int flags) {
while (1) { while (1) {
pid_t result = waitpid(pid, status, flags); pid_t result = waitpid(pid, status, flags);
@@ -540,7 +559,21 @@ void wait_for_trace(int pid, int *status, int flags) {
exit(1); 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]; char status_str[64];
parse_status(*status, status_str, sizeof(status_str)); parse_status(*status, status_str, sizeof(status_str));

View File

@@ -37,18 +37,22 @@ void free_maps(struct maps *maps);
#define REG_SP rsp #define REG_SP rsp
#define REG_IP rip #define REG_IP rip
#define REG_RET rax #define REG_RET rax
#define REG_SYSNR orig_rax
#elif defined(__i386__) #elif defined(__i386__)
#define REG_SP esp #define REG_SP esp
#define REG_IP eip #define REG_IP eip
#define REG_RET eax #define REG_RET eax
#define REG_SYSNR orig_eax
#elif defined(__aarch64__) #elif defined(__aarch64__)
#define REG_SP sp #define REG_SP sp
#define REG_IP pc #define REG_IP pc
#define REG_RET regs[0] #define REG_RET regs[0]
#define REG_SYSNR regs[8]
#elif defined(__arm__) #elif defined(__arm__)
#define REG_SP uregs[13] #define REG_SP uregs[13]
#define REG_IP uregs[15] #define REG_IP uregs[15]
#define REG_RET uregs[0] #define REG_RET uregs[0]
#define REG_SYSNR uregs[7]
#define user_regs_struct user_regs #define user_regs_struct user_regs
#endif #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); 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_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); void *find_func_addr(struct maps *local_info, struct maps *remote_info, const char *module, const char *func);

View File

@@ -112,7 +112,8 @@ mkdir "$MODPATH/webroot"
ui_print "- Extracting webroot" ui_print "- Extracting webroot"
unzip -o "$ZIPFILE" "webroot/*" -x "*.sha256" -d "$MODPATH" unzip -o "$ZIPFILE" "webroot/*" -x "*.sha256" -d "$MODPATH"
CPU_ABIS=$(getprop ro.product.cpu.abilist) CPU_ABIS=$(getprop ro.system.product.cpu.abilist)
CPU_ABIS=${CPU_ABIS:-$(getprop ro.product.cpu.abilist)}
SUPPORTS_32BIT=false SUPPORTS_32BIT=false
SUPPORTS_64BIT=false SUPPORTS_64BIT=false

View File

@@ -46,7 +46,8 @@ if [ -f $MODDIR/lib/libzygisk.so ];then
chcon u:object_r:system_file:s0 $TMP_PATH/lib/libzygisk.so chcon u:object_r:system_file:s0 $TMP_PATH/lib/libzygisk.so
fi 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 if [[ "$CPU_ABIS" == *"arm64-v8a"* || "$CPU_ABIS" == *"x86_64"* ]]; then
./bin/zygisk-ptrace64 monitor & ./bin/zygisk-ptrace64 monitor &

View File

@@ -11,6 +11,7 @@ allow zygote su {lnk_file file} read
allow zygote adb_data_file dir search allow zygote adb_data_file dir search
allow zygote adb_data_file file * allow zygote adb_data_file file *
allow zygote proc file {read open} allow zygote proc file {read open}
allow zygote nsfs file {read open}
allow zygote zygote process execmem allow zygote zygote process execmem
allow system_server system_server process execmem allow system_server system_server process execmem
allow zygote tmpfs file * allow zygote tmpfs file *

View File

@@ -91,101 +91,106 @@ async function getModuleNames(modules) {
const unameCmd = await exec('/system/bin/uname -r') const unameCmd = await exec('/system/bin/uname -r')
if (unameCmd.errno !== 0) return setError('WebUI', unameCmd.stderr) if (unameCmd.errno !== 0) return setError('WebUI', unameCmd.stderr)
document.getElementById('kernel_version_div').innerHTML = unameCmd.stdout document.getElementById('kernel_version_div').innerHTML = unameCmd.stdout.trim()
console.log('[rezygisk.js] Kernel version: ', unameCmd.stdout) console.log('[rezygisk.js] Kernel version: ', unameCmd.stdout.trim())
const catCmd = await exec('/system/bin/cat /data/adb/rezygisk/module.prop') const catCmd = await exec('/system/bin/cat /data/adb/rezygisk/module.prop')
console.log(`[rezygisk.js] ReZygisk module infomation:\n${catCmd.stdout}`) console.log(`[rezygisk.js] ReZygisk module infomation:\n${catCmd.stdout}`)
let expectedWorking = 0 if (catCmd.errno !== 0) {
let actuallyWorking = 0 console.error('[rezygisk.js] Failed to retrieve ReZygisk module information:', catCmd.stderr)
const ReZygiskInfo = { rezygisk_state.innerHTML = translations.page.home.status.notWorking
rootImpl: null, rezygisk_icon_state.innerHTML = '<img class="dimc" src="assets/cross.svg">'
monitor: null,
zygotes: [],
daemons: []
}
if (catCmd.errno === 0) { rootCss.style.setProperty('--bright', '#766000')
/* INFO: Just ensure that they won't appear unless there's info */
/* INFO: Hide zygote divs */
zygote_divs.forEach((zygote_div) => { zygote_divs.forEach((zygote_div) => {
zygote_div.style.display = 'none' zygote_div.style.display = 'none'
}) })
version.innerHTML = catCmd.stdout.split('\n').find((line) => line.startsWith('version=')).substring('version='.length).trim() loading_screen.style.display = 'none'
bottom_nav.style.display = 'flex'
let moduleInfo = catCmd.stdout.split('\n').find((line) => line.startsWith('description=')).substring('description='.length).split('[')[1].split(']')[0] return;
}
const daemonModules = [] /* INFO: Just ensure that they won't appear unless there's info */
moduleInfo.match(/\(([^)]+)\)/g).forEach((area) => { zygote_divs.forEach((zygote_div) => {
moduleInfo = moduleInfo.replace(area, ',') zygote_div.style.display = 'none'
})
const info = area.substring(1, area.length - 1).split(', ') version.innerHTML = catCmd.stdout.split('\n').find((line) => line.startsWith('version=')).substring('version='.length).trim()
if (info.length === 1) return; /* INFO: undefined as object */
const rootImpl = info[0].substring('Root: '.length) 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)
info[1] = info[1].substring('Modules: '.length) rezygisk_state.innerHTML = translations.page.home.status.notWorking
const modules = info.slice(1, info.length) rezygisk_icon_state.innerHTML = '<img class="dimc" src="assets/cross.svg">'
ReZygiskInfo.rootImpl = rootImpl rootCss.style.setProperty('--bright', '#766000')
if (modules[0] !== 'None') daemonModules.push(modules)
/* INFO: Hide zygote divs */
zygote_divs.forEach((zygote_div) => {
zygote_div.style.display = 'none'
}) })
const infoArea = moduleInfo.split(', ') loading_screen.style.display = 'none'
infoArea.forEach((info) => { bottom_nav.style.display = 'flex'
if (info.startsWith('monitor:')) {
ReZygiskInfo.monitor = info.substring('monitor: X '.length).trim()
}
if (info.startsWith('zygote')) { return;
ReZygiskInfo.zygotes.push({ }
bits: info.substring('zygote'.length, 'zygote'.length + 'XX'.length),
state: info.substring('zygoteXX: X '.length).trim()
})
}
if (info.startsWith('daemon')) { const ReZygiskState = JSON.parse(stateCmd.stdout)
ReZygiskInfo.daemons.push({
bits: info.substring('daemon'.length, 'daemon'.length + 'XX'.length),
state: info.substring('daemonXX: X '.length).trim(),
modules: daemonModules[ReZygiskInfo.daemons.length] || []
})
}
})
switch (ReZygiskInfo.monitor) { root_impl.innerHTML = ReZygiskState.root
case 'tracing': monitor_status.innerHTML = translations.page.actions.status.tracing; break;
case 'stopping': monitor_status.innerHTML = translations.page.actions.status.stopping; break; switch (ReZygiskState.monitor.state) {
case 'stopped': monitor_status.innerHTML = translations.page.actions.status.stopped; break; case 0: monitor_status.innerHTML = translations.page.actions.status.tracing; break;
case 'exiting': monitor_status.innerHTML = translations.page.actions.status.exiting; break; case 1: monitor_status.innerHTML = translations.page.actions.status.stopping; break;
default: monitor_status.innerHTML = translations.page.actions.status.unknown; 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
} }
}
expectedWorking = ReZygiskInfo.zygotes.length if (ReZygiskState.zygote['32'] !== undefined) {
const zygote32 = ReZygiskState.zygote['32']
for (let i = 0; i < ReZygiskInfo.zygotes.length; i++) { zygote_divs[1].style.display = 'block'
const zygote = ReZygiskInfo.zygotes[i]
/* INFO: Not used ATM */
/* const daemon = ReZygiskInfo.daemons[i] */
const zygoteDiv = zygote_divs[zygote.bits === '64' ? 0 : 1] switch (zygote32) {
const zygoteStatusDiv = zygote_status_divs[zygote.bits === '64' ? 0 : 1] case 1: {
zygote_status_divs[1].innerHTML = translations.page.home.info.zygote.injected
zygoteDiv.style.display = 'block' actuallyWorking++
switch (zygote.state) { break
case 'injected': {
zygoteStatusDiv.innerHTML = translations.page.home.info.zygote.injected;
actuallyWorking++
break;
}
case 'not injected': zygoteStatusDiv.innerHTML = translations.page.home.info.zygote.notInjected; break;
default: zygoteStatusDiv.innerHTML = translations.page.home.info.zygote.unknown;
} }
case 0: zygote_status_divs[1].innerHTML = translations.page.home.info.zygote.notInjected; break
default: zygote_status_divs[1].innerHTML = translations.page.home.info.zygote.unknown
} }
} }
@@ -203,24 +208,24 @@ async function getModuleNames(modules) {
rezygisk_icon_state.innerHTML = '<img class="brightc" src="assets/warn.svg">' rezygisk_icon_state.innerHTML = '<img class="brightc" src="assets/warn.svg">'
} }
if (ReZygiskInfo.rootImpl)
root_impl.innerHTML = ReZygiskInfo.rootImpl
const all_modules = [] const all_modules = []
ReZygiskInfo.daemons.forEach((daemon) => { Object.keys(ReZygiskState.rezygiskd).forEach((daemon_bit) => {
daemon.modules.forEach((module_id) => { const daemon = ReZygiskState.rezygiskd[daemon_bit]
const module = all_modules.find((mod) => mod.id === module_id)
if (module) { if (daemon.modules && daemon.modules.length > 0) {
module.bitsUsed.push(daemon.bits) daemon.modules.forEach((module_id) => {
} else { const module = all_modules.find((mod) => mod.id === module_id)
all_modules.push({ if (module) {
id: module_id, module.bitsUsed.push(daemon_bit)
name: null, } else {
bitsUsed: [ daemon.bits ] all_modules.push({
}) id: module_id,
} name: null,
}) bitsUsed: [ daemon_bit ]
})
}
})
}
}) })
if (all_modules.length !== 0) { if (all_modules.length !== 0) {
@@ -251,7 +256,6 @@ async function getModuleNames(modules) {
loading_screen.style.display = 'none' loading_screen.style.display = 'none'
bottom_nav.style.display = 'flex' bottom_nav.style.display = 'flex'
const start_time = Number(localStorage.getItem('/system/boot-time')) const start_time = Number(localStorage.getItem('/system/boot-time'))
console.log('[rezygisk.js] boot time: ', Date.now() - start_time, 'ms') console.log('[rezygisk.js] boot time: ', Date.now() - start_time, 'ms')
localStorage.removeItem('/system/boot_time') localStorage.removeItem('/system/boot_time')

93
webroot/lang/ja_JP.json Normal file
View File

@@ -0,0 +1,93 @@
{
"langName": "日本語",
"global": {
"unknown": "不明"
},
"smallPage": {
"language": {
"header": "言語を選択してください"
},
"theme": {
"header": "テーマを選択してください",
"dark": "ダーク",
"light": "ライト",
"system": "システムベース"
},
"errorh": {
"buttons": {
"copy": "コピー",
"clear": "すべてのログを消去"
},
"header": "エラーの履歴",
"placeholder": "エラーログは記録されていません!"
}
},
"page": {
"home": {
"header": "ホーム",
"status": {
"notWorking": "動作していません",
"ok": "動作中",
"partially": "部分的に動作中"
},
"info": {
"version": "バージョン",
"root": "Root の実装",
"zygote": {
"injected": "インジェクト済み",
"notInjected": "未インジェクト",
"unknown": "不明"
}
}
},
"modules": {
"header": "モジュール",
"notAvaliable": "Zygisk を使用するモジュールはありません。",
"arch": "アーキテクチャ: "
},
"actions": {
"header": "アクション",
"monitorButton": {
"start": "開始",
"stop": "停止",
"pause": "一時停止"
},
"monitor": "監視",
"status": {
"tracing": "トレース中",
"stopping": "停止中",
"stopped": "停止済み",
"exiting": "終了中",
"unknown": "不明"
}
},
"settings": {
"header": "設定",
"font": {
"header": "システムフォントを有効化",
"description": "現在の WebUI でシステムフォントの使用を有効化します。注意: FlipFont との互換性がない場合があります"
},
"theme": {
"header": "システムテーマ",
"description": "WebUI のシステムテーマを変更します"
},
"language": {
"header": "言語の変更",
"description": "言語を変更します"
},
"logs": {
"header": "エラーの履歴",
"description": "すべてのエラーログを表示します"
},
"credits": {
"module": "モジュールの開発者",
"original": "オリジナルの開発者",
"web": "WebUI の開発者"
},
"license": {
"module": "モジュールのライセンス",
"web": "WebUI のライセンス"
}
}
}
}

View File

@@ -121,11 +121,7 @@ void companion_entry(int fd) {
ASSURE_SIZE_WRITE("ZygiskdCompanion", "module_entry", ret, sizeof(uint8_t)); ASSURE_SIZE_WRITE("ZygiskdCompanion", "module_entry", ret, sizeof(uint8_t));
} }
struct sigaction sa; struct sigaction sa = { .sa_handler = SIG_IGN };
memset(&sa, 0, sizeof(sa));
sigemptyset(&sa.sa_mask);
sa.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &sa, NULL); sigaction(SIGPIPE, &sa, NULL);
while (1) { while (1) {
@@ -157,7 +153,14 @@ void companion_entry(int fd) {
LOGI("New companion request.\n - Module name: %s\n - Client fd: %d\n", name, client_fd); LOGI("New companion request.\n - Module name: %s\n - Client fd: %d\n", name, client_fd);
ret = write_uint8_t(client_fd, 1); ret = write_uint8_t(client_fd, 1);
ASSURE_SIZE_WRITE("ZygiskdCompanion", "client_fd", ret, sizeof(uint8_t)); if (ret != sizeof(uint8_t)) {
LOGE("Failed to sent client_fd in ZygiskdCompanion: Expected %zu, got %zd\n", sizeof(uint8_t), ret);
free(args);
close(client_fd);
break;
}
pthread_t thread; pthread_t thread;
if (pthread_create(&thread, NULL, entry_thread, (void *)args) == 0) if (pthread_create(&thread, NULL, entry_thread, (void *)args) == 0)

View File

@@ -62,6 +62,7 @@ void apatch_get_existence(struct root_impl_state *state) {
} }
struct package_config { struct package_config {
char *process;
uid_t uid; uid_t uid;
bool root_granted; bool root_granted;
bool umount_needed; bool umount_needed;
@@ -72,6 +73,14 @@ struct packages_config {
size_t size; size_t size;
}; };
void _apatch_free_package_config(struct packages_config *restrict config) {
for (size_t i = 0; i < config->size; i++) {
free(config->configs[i].process);
}
free(config->configs);
}
/* WARNING: Dynamic memory based */ /* WARNING: Dynamic memory based */
bool _apatch_get_package_config(struct packages_config *restrict config) { bool _apatch_get_package_config(struct packages_config *restrict config) {
config->configs = NULL; config->configs = NULL;
@@ -95,16 +104,18 @@ bool _apatch_get_package_config(struct packages_config *restrict config) {
} }
while (fgets(line, sizeof(line), fp) != NULL) { while (fgets(line, sizeof(line), fp) != NULL) {
config->configs = realloc(config->configs, (config->size + 1) * sizeof(struct package_config)); struct package_config *tmp_configs = realloc(config->configs, (config->size + 1) * sizeof(struct package_config));
if (config->configs == NULL) { if (tmp_configs == NULL) {
LOGE("Failed to realloc APatch config struct: %s\n", strerror(errno)); LOGE("Failed to realloc APatch config struct: %s\n", strerror(errno));
_apatch_free_package_config(config);
fclose(fp); fclose(fp);
return false; return false;
} }
config->configs = tmp_configs;
strtok(line, ","); config->configs[config->size].process = strdup(strtok(line, ","));
char *exclude_str = strtok(NULL, ","); char *exclude_str = strtok(NULL, ",");
if (exclude_str == NULL) continue; if (exclude_str == NULL) continue;
@@ -127,17 +138,9 @@ bool _apatch_get_package_config(struct packages_config *restrict config) {
return true; return true;
} }
void _apatch_free_package_config(struct packages_config *restrict config) {
free(config->configs);
}
bool apatch_uid_granted_root(uid_t uid) { bool apatch_uid_granted_root(uid_t uid) {
struct packages_config config; struct packages_config config;
if (!_apatch_get_package_config(&config)) { if (!_apatch_get_package_config(&config)) return false;
_apatch_free_package_config(&config);
return false;
}
for (size_t i = 0; i < config.size; i++) { for (size_t i = 0; i < config.size; i++) {
if (config.configs[i].uid != uid) continue; if (config.configs[i].uid != uid) continue;
@@ -155,13 +158,9 @@ bool apatch_uid_granted_root(uid_t uid) {
return false; return false;
} }
bool apatch_uid_should_umount(uid_t uid) { bool apatch_uid_should_umount(uid_t uid, const char *const process) {
struct packages_config config; struct packages_config config;
if (!_apatch_get_package_config(&config)) { if (!_apatch_get_package_config(&config)) return false;
_apatch_free_package_config(&config);
return false;
}
for (size_t i = 0; i < config.size; i++) { for (size_t i = 0; i < config.size; i++) {
if (config.configs[i].uid != uid) continue; if (config.configs[i].uid != uid) continue;
@@ -174,6 +173,29 @@ bool apatch_uid_should_umount(uid_t uid) {
return umount_needed; return umount_needed;
} }
/* INFO: Isolated services have different UIDs than the main app, and
while libzygisk.so has code to send the UID of the app related
to the isolated service, we add this so that in case it fails,
this should avoid it pass through as Mounted.
*/
if (IS_ISOLATED_SERVICE(uid)) {
size_t targeted_process_length = strlen(process);
for (size_t i = 0; i < config.size; i++) {
size_t config_process_length = strlen(config.configs[i].process);
size_t smallest_process_length = targeted_process_length < config_process_length ? targeted_process_length : config_process_length;
if (strncmp(config.configs[i].process, process, smallest_process_length) != 0) continue;
/* INFO: This allow us to copy the information to avoid use-after-free */
bool umount_needed = config.configs[i].umount_needed;
_apatch_free_package_config(&config);
return umount_needed;
}
}
_apatch_free_package_config(&config); _apatch_free_package_config(&config);
return false; return false;

View File

@@ -7,7 +7,7 @@ void apatch_get_existence(struct root_impl_state *state);
bool apatch_uid_granted_root(uid_t uid); bool apatch_uid_granted_root(uid_t uid);
bool apatch_uid_should_umount(uid_t uid); bool apatch_uid_should_umount(uid_t uid, const char *const process);
bool apatch_uid_is_manager(uid_t uid); bool apatch_uid_is_manager(uid_t uid);

View File

@@ -30,6 +30,7 @@ void root_impls_setup(void) {
impl.impl = Multiple; impl.impl = Multiple;
} else if (state_ksu.state == Supported) { } else if (state_ksu.state == Supported) {
impl.impl = KernelSU; impl.impl = KernelSU;
impl.variant = state_ksu.variant;
} else if (state_apatch.state == Supported) { } else if (state_apatch.state == Supported) {
impl.impl = APatch; impl.impl = APatch;
} else if (state_magisk.state == Supported) { } else if (state_magisk.state == Supported) {
@@ -73,8 +74,7 @@ void root_impls_setup(void) {
} }
void get_impl(struct root_impl *uimpl) { void get_impl(struct root_impl *uimpl) {
uimpl->impl = impl.impl; *uimpl = impl;
uimpl->variant = impl.variant;
} }
bool uid_granted_root(uid_t uid) { bool uid_granted_root(uid_t uid) {
@@ -100,7 +100,7 @@ bool uid_should_umount(uid_t uid, const char *const process) {
return ksu_uid_should_umount(uid); return ksu_uid_should_umount(uid);
} }
case APatch: { case APatch: {
return apatch_uid_should_umount(uid); return apatch_uid_should_umount(uid, process);
} }
case Magisk: { case Magisk: {
return magisk_uid_should_umount(process); return magisk_uid_should_umount(process);

View File

@@ -19,10 +19,18 @@
#define CMD_GET_VERSION 2 #define CMD_GET_VERSION 2
#define CMD_UID_GRANTED_ROOT 12 #define CMD_UID_GRANTED_ROOT 12
#define CMD_UID_SHOULD_UMOUNT 13 #define CMD_UID_SHOULD_UMOUNT 13
#define CMD_GET_MANAGER_UID 16
#define CMD_HOOK_MODE 0xC0DEAD1A
static enum kernelsu_variants variant = KOfficial;
static bool supports_manager_uid_retrieval = false;
void ksu_get_existence(struct root_impl_state *state) { void ksu_get_existence(struct root_impl_state *state) {
int reply_ok = 0;
int version = 0; int version = 0;
prctl((signed int)KERNEL_SU_OPTION, CMD_GET_VERSION, &version, 0, 0); prctl((signed int)KERNEL_SU_OPTION, CMD_GET_VERSION, &version, 0, &reply_ok);
if (version == 0) state->state = Abnormal; if (version == 0) state->state = Abnormal;
else if (version >= MIN_KSU_VERSION && version <= MAX_KSU_VERSION) { else if (version >= MIN_KSU_VERSION && version <= MAX_KSU_VERSION) {
@@ -42,6 +50,25 @@ void ksu_get_existence(struct root_impl_state *state) {
} }
state->state = Supported; state->state = Supported;
char mode[16] = { 0 };
prctl((signed int)KERNEL_SU_OPTION, CMD_HOOK_MODE, mode, NULL, &reply_ok);
if (mode[0] != '\0') state->variant = KNext;
else state->variant = KOfficial;
variant = state->variant;
/* INFO: CMD_GET_MANAGER_UID is a KernelSU Next feature, however we won't
limit to KernelSU Next only in case other forks wish to implement
it. */
prctl((signed int)KERNEL_SU_OPTION, CMD_GET_MANAGER_UID, NULL, NULL, &reply_ok);
if (reply_ok == KERNEL_SU_OPTION) {
LOGI("KernelSU implementation supports CMD_GET_MANAGER_UID.\n");
supports_manager_uid_retrieval = true;
}
} }
else if (version >= 1 && version <= MIN_KSU_VERSION - 1) state->state = TooOld; else if (version >= 1 && version <= MIN_KSU_VERSION - 1) state->state = TooOld;
else state->state = Abnormal; else state->state = Abnormal;
@@ -68,8 +95,26 @@ bool ksu_uid_should_umount(uid_t uid) {
} }
bool ksu_uid_is_manager(uid_t uid) { bool ksu_uid_is_manager(uid_t uid) {
/* INFO: If the manager UID is set, we can use it to check if the UID
is the manager UID, which is more reliable than checking
the KSU manager data directory, as spoofed builds of
KernelSU Next have different package names.
*/
if (supports_manager_uid_retrieval) {
int reply_ok = 0;
uid_t manager_uid = 0;
prctl(KERNEL_SU_OPTION, CMD_GET_MANAGER_UID, &manager_uid, NULL, &reply_ok);
return uid == manager_uid;
}
const char *manager_path = NULL;
if (variant == KOfficial) manager_path = "/data/user_de/0/me.weishu.kernelsu";
else if (variant == KNext) manager_path = "/data/user_de/0/com.rifsxd.ksunext";
struct stat s; struct stat s;
if (stat("/data/user_de/0/me.weishu.kernelsu", &s) == -1) { if (stat(manager_path, &s) == -1) {
if (errno != ENOENT) { if (errno != ENOENT) {
LOGE("Failed to stat KSU manager data directory: %s\n", strerror(errno)); LOGE("Failed to stat KSU manager data directory: %s\n", strerror(errno));
} }

View File

@@ -3,6 +3,11 @@
#include "../constants.h" #include "../constants.h"
enum kernelsu_variants {
KOfficial,
KNext
};
void ksu_get_existence(struct root_impl_state *state); void ksu_get_existence(struct root_impl_state *state);
bool ksu_uid_granted_root(uid_t uid); bool ksu_uid_granted_root(uid_t uid);

View File

@@ -28,7 +28,7 @@ char *magisk_managers[] = {
#define DEBUG_RAMDISK_MAGISK lp_select("/debug_ramdisk/magisk32", "/debug_ramdisk/magisk64") #define DEBUG_RAMDISK_MAGISK lp_select("/debug_ramdisk/magisk32", "/debug_ramdisk/magisk64")
#define BITLESS_DEBUG_RAMDISK_MAGISK "/debug_ramdisk/magisk" #define BITLESS_DEBUG_RAMDISK_MAGISK "/debug_ramdisk/magisk"
enum magisk_variants variant = Official; static enum magisk_variants variant = MOfficial;
/* INFO: Longest path */ /* INFO: Longest path */
static char path_to_magisk[sizeof(DEBUG_RAMDISK_MAGISK)] = { 0 }; static char path_to_magisk[sizeof(DEBUG_RAMDISK_MAGISK)] = { 0 };
bool is_using_sulist = false; bool is_using_sulist = false;
@@ -74,7 +74,7 @@ void magisk_get_existence(struct root_impl_state *state) {
return; return;
} }
state->variant = (uint8_t)Official; state->variant = (uint8_t)MOfficial;
for (unsigned long i = 0; i < sizeof(supported_variants) / sizeof(supported_variants[0]); i++) { for (unsigned long i = 0; i < sizeof(supported_variants) / sizeof(supported_variants[0]); i++) {
if (strstr(magisk_info, supported_variants[i])) { if (strstr(magisk_info, supported_variants[i])) {

View File

@@ -4,8 +4,8 @@
#include "../constants.h" #include "../constants.h"
enum magisk_variants { enum magisk_variants {
Official, MOfficial,
Kitsune MKitsune
}; };
void magisk_get_existence(struct root_impl_state *state); void magisk_get_existence(struct root_impl_state *state);

View File

@@ -20,6 +20,7 @@
#include "utils.h" #include "utils.h"
#include "root_impl/common.h" #include "root_impl/common.h"
#include "root_impl/kernelsu.h"
#include "root_impl/magisk.h" #include "root_impl/magisk.h"
int clean_namespace_fd = 0; int clean_namespace_fd = 0;
@@ -108,12 +109,9 @@ static void get_current_attr(char *restrict output, size_t size) {
return; return;
} }
if (fread(output, 1, size, current) == 0) { if (fread(output, 1, size, current) == 0)
LOGE("fread: %s\n", strerror(errno)); LOGE("fread: %s\n", strerror(errno));
return;
}
fclose(current); fclose(current);
} }
@@ -436,7 +434,8 @@ void stringify_root_impl_name(struct root_impl impl, char *restrict output) {
break; break;
} }
case KernelSU: { case KernelSU: {
strcpy(output, "KernelSU"); if (impl.variant == KOfficial) strcpy(output, "KernelSU");
else strcpy(output, "KernelSU Next");
break; break;
} }
@@ -446,11 +445,8 @@ void stringify_root_impl_name(struct root_impl impl, char *restrict output) {
break; break;
} }
case Magisk: { case Magisk: {
if (impl.variant == 0) { if (impl.variant == MOfficial) strcpy(output, "Magisk Official");
strcpy(output, "Magisk Official"); else strcpy(output, "Magisk Kitsune");
} else {
strcpy(output, "Magisk Kitsune");
}
break; break;
} }
@@ -544,15 +540,13 @@ bool parse_mountinfo(const char *restrict pid, struct mountinfos *restrict mount
&optional_start, &optional_end, &type_start, &type_end, &optional_start, &optional_end, &type_start, &type_end,
&source_start, &source_end, &fs_option_start, &fs_option_end); &source_start, &source_end, &fs_option_start, &fs_option_end);
mounts->mounts = (struct mountinfo *)realloc(mounts->mounts, (i + 1) * sizeof(struct mountinfo)); struct mountinfo *tmp_mounts = (struct mountinfo *)realloc(mounts->mounts, (i + 1) * sizeof(struct mountinfo));
if (!mounts->mounts) { if (!tmp_mounts) {
LOGE("Failed to allocate memory for mounts->mounts"); LOGE("Failed to allocate memory for mounts->mounts");
fclose(mountinfo); goto cleanup_mount_allocs;
free_mounts(mounts);
return false;
} }
mounts->mounts = tmp_mounts;
unsigned int shared = 0; unsigned int shared = 0;
unsigned int master = 0; unsigned int master = 0;
@@ -573,16 +567,64 @@ bool parse_mountinfo(const char *restrict pid, struct mountinfos *restrict mount
mounts->mounts[i].parent = parent; mounts->mounts[i].parent = parent;
mounts->mounts[i].device = (dev_t)(makedev(maj, min)); mounts->mounts[i].device = (dev_t)(makedev(maj, min));
mounts->mounts[i].root = strndup(line + root_start, (size_t)(root_end - root_start)); mounts->mounts[i].root = strndup(line + root_start, (size_t)(root_end - root_start));
if (mounts->mounts[i].root == NULL) {
LOGE("Failed to allocate memory for root\n");
goto cleanup_mount_allocs;
}
mounts->mounts[i].target = strndup(line + target_start, (size_t)(target_end - target_start)); mounts->mounts[i].target = strndup(line + target_start, (size_t)(target_end - target_start));
if (mounts->mounts[i].target == NULL) {
LOGE("Failed to allocate memory for target\n");
goto cleanup_root;
}
mounts->mounts[i].vfs_option = strndup(line + vfs_option_start, (size_t)(vfs_option_end - vfs_option_start)); mounts->mounts[i].vfs_option = strndup(line + vfs_option_start, (size_t)(vfs_option_end - vfs_option_start));
if (mounts->mounts[i].vfs_option == NULL) {
LOGE("Failed to allocate memory for vfs_option\n");
goto cleanup_target;
}
mounts->mounts[i].optional.shared = shared; mounts->mounts[i].optional.shared = shared;
mounts->mounts[i].optional.master = master; mounts->mounts[i].optional.master = master;
mounts->mounts[i].optional.propagate_from = propagate_from; mounts->mounts[i].optional.propagate_from = propagate_from;
mounts->mounts[i].type = strndup(line + type_start, (size_t)(type_end - type_start)); mounts->mounts[i].type = strndup(line + type_start, (size_t)(type_end - type_start));
if (mounts->mounts[i].type == NULL) {
LOGE("Failed to allocate memory for type\n");
goto cleanup_vfs_option;
}
mounts->mounts[i].source = strndup(line + source_start, (size_t)(source_end - source_start)); mounts->mounts[i].source = strndup(line + source_start, (size_t)(source_end - source_start));
if (mounts->mounts[i].source == NULL) {
LOGE("Failed to allocate memory for source\n");
goto cleanup_type;
}
mounts->mounts[i].fs_option = strndup(line + fs_option_start, (size_t)(fs_option_end - fs_option_start)); mounts->mounts[i].fs_option = strndup(line + fs_option_start, (size_t)(fs_option_end - fs_option_start));
if (mounts->mounts[i].fs_option == NULL) {
LOGE("Failed to allocate memory for fs_option\n");
goto cleanup_source;
}
i++; i++;
continue;
cleanup_source:
free((void *)mounts->mounts[i].source);
cleanup_type:
free((void *)mounts->mounts[i].type);
cleanup_vfs_option:
free((void *)mounts->mounts[i].vfs_option);
cleanup_target:
free((void *)mounts->mounts[i].target);
cleanup_root:
free((void *)mounts->mounts[i].root);
cleanup_mount_allocs:
fclose(mountinfo);
free_mounts(mounts);
return false;
} }
fclose(mountinfo); fclose(mountinfo);
@@ -603,115 +645,57 @@ bool umount_root(struct root_impl impl) {
return false; return false;
} }
switch (impl.impl) { /* INFO: "Magisk" is the longest word that will ever be put in source_name */
case None: { break; } char source_name[sizeof("magisk")];
case Multiple: { break; } if (impl.impl == KernelSU) strcpy(source_name, "KSU");
else if (impl.impl == APatch) strcpy(source_name, "APatch");
else strcpy(source_name, "magisk");
case KernelSU: LOGI("[%s] Unmounting root", source_name);
case APatch: {
char source_name[LONGEST_ROOT_IMPL_NAME];
if (impl.impl == KernelSU) strcpy(source_name, "KSU");
else strcpy(source_name, "APatch");
LOGI("[%s] Unmounting root", source_name); const char **targets_to_unmount = NULL;
size_t num_targets = 0;
const char **targets_to_unmount = NULL; for (size_t i = 0; i < mounts.length; i++) {
size_t num_targets = 0; struct mountinfo mount = mounts.mounts[i];
for (size_t i = 0; i < mounts.length; i++) { bool should_unmount = false;
struct mountinfo mount = mounts.mounts[i]; /* INFO: The root implementations have their own /system mounts, so we
only skip the mount if they are from a module, not Magisk itself.
*/
if (strncmp(mount.target, "/system/", strlen("/system/")) == 0 &&
strncmp(mount.root, "/adb/modules/", strlen("/adb/modules/")) == 0 &&
strncmp(mount.target, "/system/etc/", strlen("/system/etc/")) != 0) continue;
bool should_unmount = false; if (strcmp(mount.source, source_name) == 0) should_unmount = true;
if (strncmp(mount.target, "/data/adb/modules", strlen("/data/adb/modules")) == 0) should_unmount = true;
if (strncmp(mount.root, "/adb/modules/", strlen("/adb/modules/")) == 0) should_unmount = true;
/* INFO: KernelSU has its own /system mounts, so we only skip the mount if (!should_unmount) continue;
if they are from a module, not KSU itself.
*/
if (strncmp(mount.target, "/system/", strlen("/system/")) == 0 &&
strncmp(mount.root, "/adb/modules", strlen("/adb/modules")) == 0) continue;
if (strcmp(mount.source, source_name) == 0) should_unmount = true; num_targets++;
if (strncmp(mount.root, "/adb/modules", strlen("/adb/modules")) == 0) should_unmount = true; targets_to_unmount = realloc(targets_to_unmount, num_targets * sizeof(char*));
if (strncmp(mount.target, "/data/adb/modules", strlen("/data/adb/modules")) == 0) should_unmount = true; if (targets_to_unmount == NULL) {
LOGE("[%s] Failed to allocate memory for targets_to_unmount\n", source_name);
if (!should_unmount) continue;
num_targets++;
targets_to_unmount = realloc(targets_to_unmount, num_targets * sizeof(char*));
if (targets_to_unmount == NULL) {
LOGE("[%s] Failed to allocate memory for targets_to_unmount\n", source_name);
free(targets_to_unmount);
free_mounts(&mounts);
return false;
}
targets_to_unmount[num_targets - 1] = mount.target;
}
for (size_t i = num_targets; i > 0; i--) {
const char *target = targets_to_unmount[i - 1];
if (umount2(target, MNT_DETACH) == -1) {
LOGE("[%s] Failed to unmount %s: %s\n", source_name, target, strerror(errno));
} else {
LOGI("[%s] Unmounted %s\n", source_name, target);
}
}
free(targets_to_unmount); free(targets_to_unmount);
free_mounts(&mounts);
break; return false;
} }
case Magisk: {
LOGI("[Magisk] Unmounting root");
const char **targets_to_unmount = NULL; targets_to_unmount[num_targets - 1] = mount.target;
size_t num_targets = 0; }
for (size_t i = 0; i < mounts.length; i++) { for (size_t i = num_targets; i > 0; i--) {
struct mountinfo mount = mounts.mounts[i]; const char *target = targets_to_unmount[i - 1];
if (umount2(target, MNT_DETACH) == -1) {
bool should_unmount = false; LOGE("[%s] Failed to unmount %s: %s\n", source_name, target, strerror(errno));
/* INFO: Magisk has its own /system mounts, so we only skip the mount } else {
if they are from a module, not Magisk itself. LOGI("[%s] Unmounted %s\n", source_name, target);
*/
if (strncmp(mount.target, "/system/", strlen("/system/")) == 0 &&
strncmp(mount.root, "/adb/modules", strlen("/adb/modules")) == 0) continue;
if (strcmp(mount.source, "magisk") == 0) should_unmount = true;
if (strncmp(mount.target, "/debug_ramdisk", strlen("/debug_ramdisk")) == 0) should_unmount = true;
if (strncmp(mount.target, "/data/adb/modules", strlen("/data/adb/modules")) == 0) should_unmount = true;
if (strncmp(mount.root, "/adb/modules", strlen("/adb/modules")) == 0) should_unmount = true;
if (!should_unmount) continue;
num_targets++;
targets_to_unmount = realloc(targets_to_unmount, num_targets * sizeof(char*));
if (targets_to_unmount == NULL) {
LOGE("[Magisk] Failed to allocate memory for targets_to_unmount\n");
free(targets_to_unmount);
free_mounts(&mounts);
return false;
}
targets_to_unmount[num_targets - 1] = mount.target;
}
for (size_t i = num_targets; i > 0; i--) {
const char *target = targets_to_unmount[i - 1];
if (umount2(target, MNT_DETACH) == -1) {
LOGE("[Magisk] Failed to unmount %s: %s\n", target, strerror(errno));
} else {
LOGI("[Magisk] Unmounted %s\n", target);
}
}
free(targets_to_unmount);
break;
} }
} }
free(targets_to_unmount);
free_mounts(&mounts); free_mounts(&mounts);
@@ -844,7 +828,7 @@ int save_mns_fd(int pid, enum MountNamespaceState mns_state, struct root_impl im
return -1; return -1;
} }
if (impl.impl == Magisk && impl.variant == Kitsune && mns_state == Clean) { if (impl.impl == Magisk && impl.variant == MKitsune && mns_state == Clean) {
LOGI("[Magisk] Magisk Kitsune detected, will skip cache first."); LOGI("[Magisk] Magisk Kitsune detected, will skip cache first.");
/* INFO: MagiskSU of Kitsune has a special behavior: It is only mounted /* INFO: MagiskSU of Kitsune has a special behavior: It is only mounted

View File

@@ -65,6 +65,9 @@
return -1; \ return -1; \
} }
#define IS_ISOLATED_SERVICE(uid) \
((uid) >= 90000 && (uid) < 1000000)
#define write_func_def(type) \ #define write_func_def(type) \
ssize_t write_## type(int fd, type val) ssize_t write_## type(int fd, type val)

View File

@@ -48,11 +48,17 @@ enum Architecture {
#define ZYGISKD_PATH "/data/adb/modules/rezygisk/bin/zygiskd" lp_select("32", "64") #define ZYGISKD_PATH "/data/adb/modules/rezygisk/bin/zygiskd" lp_select("32", "64")
static enum Architecture get_arch(void) { static enum Architecture get_arch(void) {
char system_arch[32]; char system_arch[64] = { 0 };
get_property("ro.product.cpu.abilist", system_arch); get_property("ro.system.product.cpu.abilist", system_arch);
if (strstr(system_arch, "arm") != NULL) return lp_select(ARM32, ARM64); if (system_arch[0] == '\0')
get_property("ro.product.cpu.abilist", system_arch);
/* INFO: "PC" architectures should have priority because in an emulator
the native architecture should have priority over the emulated
architecture for "native" reasons. */
if (strstr(system_arch, "x86") != NULL) return lp_select(X86, X86_64); if (strstr(system_arch, "x86") != NULL) return lp_select(X86, X86_64);
if (strstr(system_arch, "arm") != NULL) return lp_select(ARM32, ARM64);
LOGE("Unsupported system architecture: %s\n", system_arch); LOGE("Unsupported system architecture: %s\n", system_arch);
exit(1); exit(1);
@@ -117,7 +123,6 @@ static void load_modules(enum Architecture arch, struct Context *restrict contex
continue; continue;
} }
context->modules = realloc(context->modules, (size_t)((context->len + 1) * sizeof(struct Module))); context->modules = realloc(context->modules, (size_t)((context->len + 1) * sizeof(struct Module)));
if (context->modules == NULL) { if (context->modules == NULL) {
LOGE("Failed reallocating memory for modules.\n"); LOGE("Failed reallocating memory for modules.\n");
@@ -130,13 +135,17 @@ static void load_modules(enum Architecture arch, struct Context *restrict contex
context->modules[context->len].companion = -1; context->modules[context->len].companion = -1;
context->len++; context->len++;
} }
closedir(dir);
} }
static void free_modules(struct Context *restrict context) { static void free_modules(struct Context *restrict context) {
for (size_t i = 0; i < context->len; i++) { for (size_t i = 0; i < context->len; i++) {
free(context->modules[i].name); free(context->modules[i].name);
if (context->modules[i].companion != -1) close(context->modules[i].companion); if (context->modules[i].companion >= 0) close(context->modules[i].companion);
} }
free(context->modules);
} }
static int create_daemon_socket(void) { static int create_daemon_socket(void) {
@@ -252,11 +261,6 @@ static int spawn_companion(char *restrict argv[], char *restrict name, int lib_f
exit(0); exit(0);
} }
struct __attribute__((__packed__)) MsgHead {
unsigned int cmd;
int length;
};
/* WARNING: Dynamic memory based */ /* WARNING: Dynamic memory based */
void zygiskd_start(char *restrict argv[]) { void zygiskd_start(char *restrict argv[]) {
/* INFO: When implementation is None or Multiple, it won't set the values /* INFO: When implementation is None or Multiple, it won't set the values
@@ -267,101 +271,63 @@ void zygiskd_start(char *restrict argv[]) {
struct root_impl impl; struct root_impl impl;
get_impl(&impl); get_impl(&impl);
if (impl.impl == None || impl.impl == Multiple) { if (impl.impl == None || impl.impl == Multiple) {
char *msg_data = NULL; unix_datagram_sendto(CONTROLLER_SOCKET, &(uint8_t){ DAEMON_SET_ERROR_INFO }, sizeof(uint8_t));
if (impl.impl == None) msg_data = "Unsupported environment: Unknown root implementation"; const char *msg = NULL;
else msg_data = "Unsupported environment: Multiple root implementations found"; if (impl.impl == None) msg = "Unsupported environment: Unknown root implementation";
else msg = "Unsupported environment: Multiple root implementations found";
struct MsgHead msg = { LOGE("%s", msg);
.cmd = DAEMON_SET_ERROR_INFO,
.length = (int)strlen(msg_data) + 1
};
unix_datagram_sendto(CONTROLLER_SOCKET, &msg, sizeof(struct MsgHead)); uint32_t msg_len = (uint32_t)strlen(msg);
unix_datagram_sendto(CONTROLLER_SOCKET, msg_data, (size_t)msg.length); unix_datagram_sendto(CONTROLLER_SOCKET, &msg_len, sizeof(msg_len));
unix_datagram_sendto(CONTROLLER_SOCKET, (void *)msg, msg_len);
free(msg_data); exit(EXIT_FAILURE);
} else { } else {
enum Architecture arch = get_arch(); enum Architecture arch = get_arch();
load_modules(arch, &context); load_modules(arch, &context);
char *module_list = NULL; unix_datagram_sendto(CONTROLLER_SOCKET, &(uint8_t){ DAEMON_SET_INFO }, sizeof(uint8_t));
size_t module_list_len = 0;
if (context.len == 0) {
module_list = strdup("None");
module_list_len = strlen("None");
} else {
for (size_t i = 0; i < context.len; i++) {
if (i != context.len - 1) {
module_list = realloc(module_list, module_list_len + strlen(context.modules[i].name) + strlen(", ") + 1);
if (module_list == NULL) {
LOGE("Failed reallocating memory for module list.\n");
return;
}
strcpy(module_list + module_list_len, context.modules[i].name);
module_list_len += strlen(context.modules[i].name);
strcpy(module_list + module_list_len, ", ");
module_list_len += strlen(", ");
} else {
module_list = realloc(module_list, module_list_len + strlen(context.modules[i].name) + 1);
if (module_list == NULL) {
LOGE("Failed reallocating memory for module list.\n");
return;
}
strcpy(module_list + module_list_len, context.modules[i].name);
module_list_len += strlen(context.modules[i].name);
}
}
}
char impl_name[LONGEST_ROOT_IMPL_NAME]; char impl_name[LONGEST_ROOT_IMPL_NAME];
stringify_root_impl_name(impl, impl_name); stringify_root_impl_name(impl, impl_name);
size_t msg_length = strlen("Root: , Modules: ") + strlen(impl_name) + module_list_len + 1; uint32_t root_impl_len = (uint32_t)strlen(impl_name);
unix_datagram_sendto(CONTROLLER_SOCKET, &root_impl_len, sizeof(root_impl_len));
unix_datagram_sendto(CONTROLLER_SOCKET, impl_name, root_impl_len);
struct MsgHead msg = { uint32_t modules_len = (uint32_t)context.len;
.cmd = DAEMON_SET_INFO, unix_datagram_sendto(CONTROLLER_SOCKET, &modules_len, sizeof(modules_len));
.length = (int)msg_length
};
char *msg_data = malloc(msg_length); for (size_t i = 0; i < context.len; i++) {
if (msg_data == NULL) { uint32_t module_name_len = (uint32_t)strlen(context.modules[i].name);
LOGE("Failed allocating memory for message data.\n"); unix_datagram_sendto(CONTROLLER_SOCKET, &module_name_len, sizeof(module_name_len));
unix_datagram_sendto(CONTROLLER_SOCKET, context.modules[i].name, module_name_len);
return;
} }
snprintf(msg_data, msg_length, "Root: %s, Modules: %s", impl_name, module_list); LOGI("Sent root implementation and modules information to controller socket");
unix_datagram_sendto(CONTROLLER_SOCKET, &msg, sizeof(struct MsgHead));
unix_datagram_sendto(CONTROLLER_SOCKET, msg_data, msg_length);
free(msg_data);
free(module_list);
} }
int socket_fd = create_daemon_socket(); int socket_fd = create_daemon_socket();
if (socket_fd == -1) { if (socket_fd == -1) {
LOGE("Failed creating daemon socket\n"); LOGE("Failed creating daemon socket\n");
free_modules(&context);
return; return;
} }
struct sigaction sa = { .sa_handler = SIG_IGN };
sigaction(SIGPIPE, &sa, NULL);
bool first_process = true; bool first_process = true;
while (1) { while (1) {
int client_fd = accept(socket_fd, NULL, NULL); int client_fd = accept(socket_fd, NULL, NULL);
if (client_fd == -1) { if (client_fd == -1) {
LOGE("accept: %s\n", strerror(errno)); LOGE("accept: %s\n", strerror(errno));
return; break;
} }
uint8_t action8 = 0; uint8_t action8 = 0;
@@ -369,53 +335,33 @@ void zygiskd_start(char *restrict argv[]) {
if (len == -1) { if (len == -1) {
LOGE("read: %s\n", strerror(errno)); LOGE("read: %s\n", strerror(errno));
return; break;
} else if (len == 0) { } else if (len == 0) {
LOGI("Client disconnected\n"); LOGI("Client disconnected\n");
return; break;
} }
enum DaemonSocketAction action = (enum DaemonSocketAction)action8; enum DaemonSocketAction action = (enum DaemonSocketAction)action8;
switch (action) { switch (action) {
case PingHeartbeat: { case PingHeartbeat: {
struct MsgHead msg = { unix_datagram_sendto(CONTROLLER_SOCKET, &(uint8_t){ ZYGOTE_INJECTED }, sizeof(uint8_t));
.cmd = ZYGOTE_INJECTED,
.length = 0
};
unix_datagram_sendto(CONTROLLER_SOCKET, &msg, sizeof(struct MsgHead));
break; break;
} }
case ZygoteRestart: { case ZygoteRestart: {
for (size_t i = 0; i < context.len; i++) { for (size_t i = 0; i < context.len; i++) {
if (context.modules[i].companion != -1) { if (context.modules[i].companion <= -1) continue;
close(context.modules[i].companion);
context.modules[i].companion = -1; close(context.modules[i].companion);
} context.modules[i].companion = -1;
} }
break; break;
} }
case SystemServerStarted: { case SystemServerStarted: {
struct MsgHead msg = { unix_datagram_sendto(CONTROLLER_SOCKET, &(uint8_t){ SYSTEM_SERVER_STARTED }, sizeof(uint8_t));
.cmd = SYSTEM_SERVER_STARTED,
.length = 0
};
unix_datagram_sendto(CONTROLLER_SOCKET, &msg, sizeof(struct MsgHead));
if (impl.impl == None || impl.impl == Multiple) {
LOGI("Unsupported environment detected. Exiting.\n");
close(client_fd);
close(socket_fd);
free_modules(&context);
exit(1);
}
break; break;
} }
@@ -555,9 +501,19 @@ void zygiskd_start(char *restrict argv[]) {
ssize_t ret = read_size_t(client_fd, &index); ssize_t ret = read_size_t(client_fd, &index);
ASSURE_SIZE_READ_BREAK("RequestCompanionSocket", "index", ret, sizeof(index)); ASSURE_SIZE_READ_BREAK("RequestCompanionSocket", "index", ret, sizeof(index));
struct Module *module = &context.modules[index]; if (index >= context.len) {
LOGE("Invalid module index: %zu\n", index);
if (module->companion != -1) { ret = write_uint8_t(client_fd, 0);
ASSURE_SIZE_WRITE_BREAK("RequestCompanionSocket", "response", ret, sizeof(int));
close(client_fd);
break;
}
struct Module *module = &context.modules[index];
if (module->companion >= 0) {
if (!check_unix_socket(module->companion, false)) { if (!check_unix_socket(module->companion, false)) {
LOGE(" - Companion for module \"%s\" crashed\n", module->name); LOGE(" - Companion for module \"%s\" crashed\n", module->name);
@@ -566,10 +522,10 @@ void zygiskd_start(char *restrict argv[]) {
} }
} }
if (module->companion == -1) { if (module->companion <= -1) {
module->companion = spawn_companion(argv, module->name, module->lib_fd); module->companion = spawn_companion(argv, module->name, module->lib_fd);
if (module->companion > 0) { if (module->companion >= 0) {
LOGI(" - Spawned companion for \"%s\": %d\n", module->name, module->companion); LOGI(" - Spawned companion for \"%s\": %d\n", module->name, module->companion);
} else { } else {
if (module->companion == -2) { if (module->companion == -2) {
@@ -586,7 +542,7 @@ void zygiskd_start(char *restrict argv[]) {
so just sending the file descriptor of the client is so just sending the file descriptor of the client is
safe. safe.
*/ */
if (module->companion != -1) { if (module->companion >= 0) {
LOGI(" - Sending companion fd socket of module \"%s\"\n", module->name); LOGI(" - Sending companion fd socket of module \"%s\"\n", module->name);
if (write_fd(module->companion, client_fd) == -1) { if (write_fd(module->companion, client_fd) == -1) {
@@ -618,6 +574,17 @@ void zygiskd_start(char *restrict argv[]) {
ssize_t ret = read_size_t(client_fd, &index); ssize_t ret = read_size_t(client_fd, &index);
ASSURE_SIZE_READ_BREAK("GetModuleDir", "index", ret, sizeof(index)); ASSURE_SIZE_READ_BREAK("GetModuleDir", "index", ret, sizeof(index));
if (index >= context.len) {
LOGE("Invalid module index: %zu\n", index);
ret = write_uint8_t(client_fd, 0);
ASSURE_SIZE_WRITE_BREAK("GetModuleDir", "response", ret, sizeof(int));
close(client_fd);
break;
}
char module_dir[PATH_MAX]; char module_dir[PATH_MAX];
snprintf(module_dir, PATH_MAX, "%s/%s", PATH_MODULES_DIR, context.modules[index].name); snprintf(module_dir, PATH_MAX, "%s/%s", PATH_MODULES_DIR, context.modules[index].name);