Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 7 additions & 9 deletions native/src/core/zygisk/daemon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,19 +107,17 @@ impl ZygiskState {
Ok(())
}

pub fn reset(&mut self, mut restore: bool) {
pub fn reset(&mut self, restore: bool) {
if restore {
self.start_count = 1;
} else {
self.sockets = (None, None);
self.start_count += 1;
if self.start_count > 3 {
warn!("zygote crashed too many times, rolling-back");
restore = true;
}
self.set_prop();
return;
}

if restore {
self.sockets = (None, None);
self.start_count += 1;
if self.start_count > 3 {
warn!("zygote crashed too many times, rolling-back");
self.restore_prop();
} else {
self.set_prop();
Expand Down
20 changes: 20 additions & 0 deletions native/src/core/zygisk/exec_spawn_replay.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#pragma once

#include <cstddef>
#include <cstdint>
#include <span>

namespace zygisk {

inline constexpr std::size_t kGrapheneOsNativeForkFlagsIndex = 1;
inline constexpr std::int64_t kGrapheneOsUseZygoteSpawning = 1LL << 3;

inline bool is_grapheneos_exec_spawn_replay_contract(
std::int64_t native_fork_flags, std::span<const int> fds_to_close) {
return (native_fork_flags & kGrapheneOsUseZygoteSpawning) == 0 &&
fds_to_close.size() == 2 &&
fds_to_close[0] == -1 &&
fds_to_close[1] == -1;
}

}
36 changes: 32 additions & 4 deletions native/src/core/zygisk/gen_jni_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ def cpp(self) -> str:
class Anon(Argument):
cnt = 0

def __init__(self, type: JType):
super().__init__(f"_{Anon.cnt}", type)
def __init__(self, type: JType, name=None):
super().__init__(name or f"_{Anon.cnt}", type)
Anon.cnt += 1


Expand Down Expand Up @@ -131,6 +131,34 @@ def body(self, orig_fn_ptr: str):
return decl


class ForkGrapheneOsC(ForkApp):
def body(self, orig_fn_ptr: str):
decl = ""
decl += ind(3) + self.init_args()
for a in self.args:
if a.set_arg:
decl += ind(3) + f"args.{a.name} = &{a.name};"
decl += ind(3) + "ZygiskContext ctx(env, &args);"
decl += ind(3) + (
"if (is_grapheneos_exec_spawn_replay("
"env, grapheneos_extra_args, fds_to_close)) {"
)
decl += ind(4) + "ctx.nativeForkAndSpecialize_in_place_pre();"
decl += ind(4) + f"jint result = reinterpret_cast<{self.cpp_fn_type()})>({orig_fn_ptr})("
decl += ind(5) + self.arg_list_name()
decl += ind(4) + ");"
decl += ind(4) + "ctx.nativeForkAndSpecialize_in_place_post(result == 0);"
decl += ind(4) + "return result;"
decl += ind(3) + "}"
decl += ind(3) + "ctx.nativeForkAndSpecialize_pre();"
decl += ind(3) + f"reinterpret_cast<{self.cpp_fn_type()})>({orig_fn_ptr})("
decl += ind(4) + self.arg_list_name()
decl += ind(3) + ");"
decl += ind(3) + "ctx.nativeForkAndSpecialize_post();"
decl += ind(3) + "return ctx.pid;"
return decl


class SpecializeApp(ForkApp):
def __init__(self, ver: str, args: list[Argument]):
super().__init__(ver, args)
Expand Down Expand Up @@ -507,10 +535,10 @@ def init_args(self):
)

# GrapheneOS C (extra args moved to the first parameter)
fas_grapheneos_c = ForkApp(
fas_grapheneos_c = ForkGrapheneOsC(
"grapheneos_c",
[
Anon(jlongArray),
Anon(jlongArray, "grapheneos_extra_args"),
uid,
gid,
gids,
Expand Down
12 changes: 6 additions & 6 deletions native/src/core/zygisk/hook.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,6 @@ DCL_HOOK_FUNC(static void, android_log_close) {
// It should be safe to assume all dlclose's in libnativebridge are for zygisk_loader
DCL_HOOK_FUNC(static int, dlclose, void *handle) {
if (!g_hook->self_handle) {
ZLOGV("dlclose zygisk_loader\n");
g_hook->post_native_bridge_load(handle);
}
return 0;
Expand Down Expand Up @@ -368,8 +367,9 @@ void HookContext::post_native_bridge_load(void *handle) {
return _URC_NO_REASON;
}, &arg);

if (!arg.load_native_bridge || !arg.callbacks)
if (!arg.load_native_bridge || !arg.callbacks) {
return;
}

// Reload the real native bridge if necessary
auto nb = get_prop(NBPROP);
Expand Down Expand Up @@ -421,8 +421,9 @@ void HookContext::hook_plt() {
PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, strdup);
PLT_HOOK_REGISTER_SYM(android_runtime_dev, android_runtime_inode, "__android_log_close", android_log_close);

if (!lsplt::CommitHook())
if (!lsplt::CommitHook()) {
ZLOGE("plt_hook failed\n");
}

// Remove unhooked methods
std::erase_if(plt_backup, [](auto &t) { return *std::get<3>(t) == nullptr; });
Expand Down Expand Up @@ -474,7 +475,8 @@ static void register_jni_methods(JNIEnv *env, jclass clazz, JNIMethods methods)
if (!method.fnPtr) continue;

// It's normal that the method is not found
if (env->RegisterNatives(clazz, &method, 1) == JNI_ERR || env->ExceptionCheck() == JNI_TRUE) {
if (env->RegisterNatives(clazz, &method, 1) == JNI_ERR ||
env->ExceptionCheck() == JNI_TRUE) {
env->ExceptionClear();
method.fnPtr = nullptr;
}
Expand Down Expand Up @@ -506,8 +508,6 @@ int HookContext::hook_jni_methods(JNIEnv *env, jclass clazz, JNIMethods methods)
for (const auto &old_method : old_methods) {
if (strcmp(old_method.name, new_method.name) == 0 &&
strcmp(old_method.signature, new_method.signature) == 0) {
ZLOGV("replace %s %s %p -> %p\n",
method.name, method.signature, old_method.fnPtr, method.fnPtr);
method.fnPtr = old_method.fnPtr;
++hook_count;
// Break 2 levels of for loop
Expand Down
14 changes: 11 additions & 3 deletions native/src/core/zygisk/jni_hooks.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ std::array<JNINativeMethod, 16> fork_app_methods = {{
"nativeForkAndSpecialize",
// ILIILILILLLLZLLZZLLZZZ
"([JII[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;ZZ[Ljava/lang/String;[Ljava/lang/String;ZZZ)I",
(void *) +[] [[clang::no_stack_protector]] (JNIEnv *env, jclass clazz, jlongArray _11, 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, jboolean use_fifo_ui, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs, jboolean mount_sysprop_overrides) static -> jint {
(void *) +[] [[clang::no_stack_protector]] (JNIEnv *env, jclass clazz, jlongArray grapheneos_extra_args, 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, jboolean use_fifo_ui, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs, jboolean mount_sysprop_overrides) static -> jint {
AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
args.fds_to_ignore = &fds_to_ignore;
args.is_child_zygote = &is_child_zygote;
Expand All @@ -327,9 +327,17 @@ std::array<JNINativeMethod, 16> fork_app_methods = {{
args.mount_storage_dirs = &mount_storage_dirs;
args.mount_sysprop_overrides = &mount_sysprop_overrides;
ZygiskContext ctx(env, &args);
if (is_grapheneos_exec_spawn_replay(env, grapheneos_extra_args, fds_to_close)) {
ctx.nativeForkAndSpecialize_in_place_pre();
jint result = reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jlongArray grapheneos_extra_args, 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, jboolean use_fifo_ui, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs, jboolean mount_sysprop_overrides)>(get_defs()->fork_app_methods[15].fnPtr)(
env, clazz, grapheneos_extra_args, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir, is_top_app, use_fifo_ui, pkg_data_info_list, whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs, mount_sysprop_overrides
);
ctx.nativeForkAndSpecialize_in_place_post(result == 0);
return result;
}
ctx.nativeForkAndSpecialize_pre();
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jlongArray _11, 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, jboolean use_fifo_ui, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs, jboolean mount_sysprop_overrides)>(get_defs()->fork_app_methods[15].fnPtr)(
env, clazz, _11, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir, is_top_app, use_fifo_ui, pkg_data_info_list, whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs, mount_sysprop_overrides
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jlongArray grapheneos_extra_args, 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, jboolean use_fifo_ui, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs, jboolean mount_sysprop_overrides)>(get_defs()->fork_app_methods[15].fnPtr)(
env, clazz, grapheneos_extra_args, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir, is_top_app, use_fifo_ui, pkg_data_info_list, whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs, mount_sysprop_overrides
);
ctx.nativeForkAndSpecialize_post();
return ctx.pid;
Expand Down
46 changes: 46 additions & 0 deletions native/src/core/zygisk/module.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
#include <sys/mman.h>
#include <android/dlext.h>
#include <dlfcn.h>
#include <array>

#include <lsplt.hpp>

#include <base.hpp>

#include "zygisk.hpp"
#include "exec_spawn_replay.hpp"
#include "module.hpp"

using namespace std;
Expand Down Expand Up @@ -293,6 +295,28 @@ void ZygiskContext::sanitize_fds() {
}
}

bool is_grapheneos_exec_spawn_replay(
JNIEnv *env, jlongArray grapheneos_extra_args, jintArray fds_to_close) {
if (grapheneos_extra_args == nullptr || fds_to_close == nullptr ||
env->GetArrayLength(grapheneos_extra_args) <=
static_cast<jsize>(zygisk::kGrapheneOsNativeForkFlagsIndex) ||
env->GetArrayLength(fds_to_close) != 2) {
return false;
}

jlong native_fork_flags = 0;
env->GetLongArrayRegion(
grapheneos_extra_args,
static_cast<jsize>(zygisk::kGrapheneOsNativeForkFlagsIndex),
1,
&native_fork_flags);
std::array<jint, 2> close_fds{};
env->GetIntArrayRegion(
fds_to_close, 0, static_cast<jsize>(close_fds.size()), close_fds.data());
return zygisk::is_grapheneos_exec_spawn_replay_contract(
native_fork_flags, std::span<const int>(close_fds.data(), close_fds.size()));
}

bool ZygiskContext::exempt_fd(int fd) {
if ((flags & POST_SPECIALIZE) || (flags & SKIP_CLOSE_LOG_PIPE))
return true;
Expand Down Expand Up @@ -322,6 +346,10 @@ void ZygiskContext::fork_pre() {
if (!is_child())
return;

record_open_fds();
}

void ZygiskContext::record_open_fds() {
// Record all open fds
auto dir = xopen_dir("/proc/self/fd");
for (dirent *entry; (entry = xreaddir(dir.get()));) {
Expand Down Expand Up @@ -498,3 +526,21 @@ void ZygiskContext::nativeForkAndSpecialize_post() {
}
fork_post();
}

void ZygiskContext::nativeForkAndSpecialize_in_place_pre() {
process = env->GetStringUTFChars(args.app->nice_name, nullptr);
ZLOGV("pre forkAndSpecialize in-place [%s]\n", process);
flags |= APP_FORK_AND_SPECIALIZE;
record_open_fds();
app_specialize_pre();
sanitize_fds();
}

void ZygiskContext::nativeForkAndSpecialize_in_place_post(bool specialized) {
if (specialized) {
ZLOGV("post forkAndSpecialize in-place [%s]\n", process);
app_specialize_post();
} else {
env->ReleaseStringUTFChars(args.app->nice_name, process);
}
}
6 changes: 6 additions & 0 deletions native/src/core/zygisk/module.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,9 @@ struct ZygiskModule {
extern ZygiskContext *g_ctx;
extern int (*old_fork)(void);

bool is_grapheneos_exec_spawn_replay(
JNIEnv *env, jlongArray grapheneos_extra_args, jintArray fds_to_close);

enum : uint32_t {
POST_SPECIALIZE = (1u << 0),
APP_FORK_AND_SPECIALIZE = (1u << 1),
Expand Down Expand Up @@ -268,8 +271,11 @@ struct ZygiskContext {
DCL_PRE_POST(nativeForkAndSpecialize)
DCL_PRE_POST(nativeSpecializeAppProcess)
DCL_PRE_POST(nativeForkSystemServer)
void nativeForkAndSpecialize_in_place_pre();
void nativeForkAndSpecialize_in_place_post(bool specialized);

int get_module_info(int uid, rust::Vec<int> &fds);
void record_open_fds();
void sanitize_fds();
bool exempt_fd(int fd);
bool can_exempt_fd() const;
Expand Down
Loading
Loading