Refactor gen_jni_hooks.py

[skip ci]
This commit is contained in:
topjohnwu
2025-12-07 02:25:50 -08:00
parent 0936cdb192
commit dd42aa99ea

View File

@@ -1,249 +1,588 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
primitives = ['jint', 'jboolean', 'jlong'] primitives = ["jint", "jboolean", "jlong"]
class JType: class JType:
def __init__(self, cpp, jni): def __init__(self, cpp: str, jni: str):
self.cpp = cpp self.cpp = cpp
self.jni = jni self.jni = jni
class JArray(JType): class JArray(JType):
def __init__(self, type): def __init__(self, type: JType):
if type.cpp in primitives: if type.cpp in primitives:
name = type.cpp + 'Array' name = type.cpp + "Array"
else: else:
name = 'jobjectArray' name = "jobjectArray"
super().__init__(name, '[' + type.jni) super().__init__(name, "[" + type.jni)
class Argument: class Argument:
def __init__(self, name, type, set_arg = False): def __init__(self, name: str, type: JType, set_arg=False):
self.name = name self.name = name
self.type = type self.type = type
self.set_arg = set_arg self.set_arg = set_arg
def cpp(self): def cpp(self) -> str:
return f'{self.type.cpp} {self.name}' return f"{self.type.cpp} {self.name}"
# Args we don't care, give it an auto generated name # Args we don't care, give it an auto generated name
class Anon(Argument): class Anon(Argument):
cnt = 0 cnt = 0
def __init__(self, type):
super().__init__(f'_{Anon.cnt}', type) def __init__(self, type: JType):
super().__init__(f"_{Anon.cnt}", type)
Anon.cnt += 1 Anon.cnt += 1
class Return: class Return:
def __init__(self, value, type): def __init__(self, value: str, type: JType):
self.value = value self.value = value
self.type = type self.type = type
class Method:
def __init__(self, name, ret, args): class JNIMethod:
def __init__(self, name: str, ret: Return, args: list[Argument]):
self.name = name self.name = name
self.ret = ret self.ret = ret
self.args = args self.args = args
def cpp(self): def arg_list_name(self) -> str:
return ', '.join(map(lambda x: x.cpp(), self.args)) return "env, clazz, " + ", ".join(map(lambda x: x.name, self.args))
def name_list(self): def arg_list_cpp(self) -> str:
return ', '.join(map(lambda x: x.name, self.args)) return "JNIEnv *env, jclass clazz, " + ", ".join(
map(lambda x: x.cpp(), self.args)
)
def jni(self): def cpp_fn_type(self) -> str:
args = ''.join(map(lambda x: x.type.jni, self.args)) return f"{self.ret.type.cpp}(*)({self.arg_list_cpp()}"
return f'({args}){self.ret.type.jni}'
def body(self, name, i): def cpp_lambda_sig(self) -> str:
return '' return f"[] [[clang::no_stack_protector]] ({self.arg_list_cpp()}) static -> {self.ret.type.cpp}"
class JNIHook(Method): def jni_sig(self):
def __init__(self, ver, ret, args): args = "".join(map(lambda x: x.type.jni, self.args))
name = f'{self.base_name()}_{ver}' return f"({args}){self.ret.type.jni}"
class JNIHook(JNIMethod):
def __init__(self, ver: str, ret: Return, args: list[Argument]):
name = f"{self.hook_target()}_{ver}"
super().__init__(name, ret, args) super().__init__(name, ret, args)
def base_name(self): def hook_target(self):
return '' return ""
def body(self, orig_fn_ptr: str):
return ""
def orig_method(self, field, i):
return f'reinterpret_cast<{self.ret.type.cpp}(*)(JNIEnv *env, jclass clazz, {self.cpp()})>(g_hook->{field}[{i}].fnPtr)'
def ind(i): def ind(i):
return '\n' + ' ' * i return "\n" + " " * i
# Common types # Common types
jint = JType('jint', 'I') jint = JType("jint", "I")
jintArray = JArray(jint) jintArray = JArray(jint)
jstring = JType('jstring', 'Ljava/lang/String;') jstring = JType("jstring", "Ljava/lang/String;")
jboolean = JType('jboolean', 'Z') jboolean = JType("jboolean", "Z")
jlong = JType('jlong', 'J') jlong = JType("jlong", "J")
void = JType('void', 'V') void = JType("void", "V")
class ForkAndSpec(JNIHook):
class ForkApp(JNIHook):
def __init__(self, ver, args): def __init__(self, ver, args):
super().__init__(ver, Return('ctx.pid', jint), args) super().__init__(ver, Return("ctx.pid", jint), args)
def base_name(self): def hook_target(self):
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 "AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);"
def body(self, field, i): def body(self, orig_fn_ptr: str):
decl = '' decl = ""
decl += ind(3) + self.init_args() decl += ind(3) + self.init_args()
for a in self.args: for a in self.args:
if a.set_arg: if a.set_arg:
decl += ind(3) + f'args.{a.name} = &{a.name};' decl += ind(3) + f"args.{a.name} = &{a.name};"
decl += ind(3) + 'ZygiskContext ctx(env, &args);' decl += ind(3) + "ZygiskContext ctx(env, &args);"
decl += ind(3) + f'ctx.{self.base_name()}_pre();' decl += ind(3) + f"ctx.{self.hook_target()}_pre();"
decl += ind(3) + self.orig_method(field, i) + '(' decl += ind(3) + f"reinterpret_cast<{self.cpp_fn_type()})>({orig_fn_ptr})("
decl += ind(4) + f'env, clazz, {self.name_list()}' decl += ind(4) + self.arg_list_name()
decl += ind(3) + ');' decl += ind(3) + ");"
decl += ind(3) + f'ctx.{self.base_name()}_post();' decl += ind(3) + f"ctx.{self.hook_target()}_post();"
if self.ret.value:
decl += ind(3) + f"return {self.ret.value};"
return decl return decl
class SpecApp(ForkAndSpec):
def __init__(self, ver, args): class SpecializeApp(ForkApp):
def __init__(self, ver: str, args: list[Argument]):
super().__init__(ver, args) super().__init__(ver, args)
self.ret = Return('', void) self.ret = Return("", void)
def base_name(self): def hook_target(self):
return 'nativeSpecializeAppProcess' return "nativeSpecializeAppProcess"
class ForkServer(ForkAndSpec):
def base_name(self): class ForkServer(ForkApp):
return 'nativeForkSystemServer' def hook_target(self):
return "nativeForkSystemServer"
def init_args(self): def init_args(self):
return 'ServerSpecializeArgs_v1 args(uid, gid, gids, runtime_flags, permitted_capabilities, effective_capabilities);' return "ServerSpecializeArgs_v1 args(uid, gid, gids, runtime_flags, permitted_capabilities, effective_capabilities);"
# Common args # Common args
uid = Argument('uid', jint) uid = Argument("uid", jint)
gid = Argument('gid', jint) gid = Argument("gid", jint)
gids = Argument('gids', jintArray) gids = Argument("gids", jintArray)
runtime_flags = Argument('runtime_flags', jint) runtime_flags = Argument("runtime_flags", jint)
rlimits = Argument('rlimits', JArray(jintArray)) rlimits = Argument("rlimits", JArray(jintArray))
mount_external = Argument('mount_external', jint) mount_external = Argument("mount_external", jint)
se_info = Argument('se_info', jstring) se_info = Argument("se_info", jstring)
nice_name = Argument('nice_name', jstring) nice_name = Argument("nice_name", jstring)
fds_to_close = Argument('fds_to_close', jintArray) fds_to_close = Argument("fds_to_close", jintArray)
instruction_set = Argument('instruction_set', jstring) instruction_set = Argument("instruction_set", jstring)
app_data_dir = Argument('app_data_dir', jstring) app_data_dir = Argument("app_data_dir", jstring)
# o # o
fds_to_ignore = Argument('fds_to_ignore', jintArray, True) fds_to_ignore = Argument("fds_to_ignore", jintArray, True)
# p # p
is_child_zygote = Argument('is_child_zygote', jboolean, True) is_child_zygote = Argument("is_child_zygote", jboolean, True)
# q_alt # q_alt
is_top_app = Argument('is_top_app', jboolean, True) is_top_app = Argument("is_top_app", jboolean, True)
# r # r
pkg_data_info_list = Argument('pkg_data_info_list', JArray(jstring), True) pkg_data_info_list = Argument("pkg_data_info_list", JArray(jstring), True)
whitelisted_data_info_list = Argument('whitelisted_data_info_list', JArray(jstring), True) whitelisted_data_info_list = Argument(
mount_data_dirs = Argument('mount_data_dirs', jboolean, True) "whitelisted_data_info_list", JArray(jstring), True
mount_storage_dirs = Argument('mount_storage_dirs', jboolean, True) )
mount_data_dirs = Argument("mount_data_dirs", jboolean, True)
mount_storage_dirs = Argument("mount_storage_dirs", jboolean, True)
# u # u
mount_sysprop_overrides = Argument('mount_sysprop_overrides', jboolean, True) mount_sysprop_overrides = Argument("mount_sysprop_overrides", jboolean, True)
# b # b
use_fifo_ui = Argument('use_fifo_ui', jboolean, False) use_fifo_ui = Argument("use_fifo_ui", jboolean, False)
# server # server
permitted_capabilities = Argument('permitted_capabilities', jlong) permitted_capabilities = Argument("permitted_capabilities", jlong)
effective_capabilities = Argument('effective_capabilities', jlong) effective_capabilities = Argument("effective_capabilities", jlong)
# Method definitions # Method definitions
fas_l = ForkAndSpec('l', [uid, gid, gids, runtime_flags, rlimits, mount_external, fas_l = ForkApp(
se_info, nice_name, fds_to_close, instruction_set, app_data_dir]) "l",
[
uid,
gid,
gids,
runtime_flags,
rlimits,
mount_external,
se_info,
nice_name,
fds_to_close,
instruction_set,
app_data_dir,
],
)
fas_o = ForkAndSpec('o', [uid, gid, gids, runtime_flags, rlimits, mount_external, fas_o = ForkApp(
se_info, nice_name, fds_to_close, fds_to_ignore, instruction_set, app_data_dir]) "o",
[
uid,
gid,
gids,
runtime_flags,
rlimits,
mount_external,
se_info,
nice_name,
fds_to_close,
fds_to_ignore,
instruction_set,
app_data_dir,
],
)
fas_p = ForkAndSpec('p', [uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, fas_p = ForkApp(
nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir]) "p",
[
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,
],
)
fas_q_alt = ForkAndSpec('q_alt', [uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, fas_q_alt = ForkApp(
nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir, is_top_app]) "q_alt",
[
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,
],
)
fas_r = ForkAndSpec('r', [uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, fas_r = ForkApp(
nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir, is_top_app, "r",
pkg_data_info_list, whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs]) [
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,
pkg_data_info_list,
whitelisted_data_info_list,
mount_data_dirs,
mount_storage_dirs,
],
)
fas_u = ForkAndSpec('u', [uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, fas_u = ForkApp(
nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir, is_top_app, "u",
pkg_data_info_list, whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs, mount_sysprop_overrides]) [
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,
pkg_data_info_list,
whitelisted_data_info_list,
mount_data_dirs,
mount_storage_dirs,
mount_sysprop_overrides,
],
)
fas_b = ForkAndSpec('b', [uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, fas_b = ForkApp(
nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir, is_top_app, use_fifo_ui, "b",
pkg_data_info_list, whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs, mount_sysprop_overrides]) [
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,
],
)
fas_samsung_m = ForkAndSpec('samsung_m', [uid, gid, gids, runtime_flags, rlimits, mount_external, fas_samsung_m = ForkApp(
se_info, Anon(jint), Anon(jint), nice_name, fds_to_close, instruction_set, app_data_dir]) "samsung_m",
[
uid,
gid,
gids,
runtime_flags,
rlimits,
mount_external,
se_info,
Anon(jint),
Anon(jint),
nice_name,
fds_to_close,
instruction_set,
app_data_dir,
],
)
fas_samsung_n = ForkAndSpec('samsung_n', [uid, gid, gids, runtime_flags, rlimits, mount_external, fas_samsung_n = ForkApp(
se_info, Anon(jint), Anon(jint), nice_name, fds_to_close, instruction_set, app_data_dir, Anon(jint)]) "samsung_n",
[
uid,
gid,
gids,
runtime_flags,
rlimits,
mount_external,
se_info,
Anon(jint),
Anon(jint),
nice_name,
fds_to_close,
instruction_set,
app_data_dir,
Anon(jint),
],
)
fas_samsung_o = ForkAndSpec('samsung_o', [uid, gid, gids, runtime_flags, rlimits, mount_external, fas_samsung_o = ForkApp(
se_info, Anon(jint), Anon(jint), nice_name, fds_to_close, fds_to_ignore, instruction_set, app_data_dir]) "samsung_o",
[
uid,
gid,
gids,
runtime_flags,
rlimits,
mount_external,
se_info,
Anon(jint),
Anon(jint),
nice_name,
fds_to_close,
fds_to_ignore,
instruction_set,
app_data_dir,
],
)
fas_samsung_p = ForkAndSpec('samsung_p', [uid, gid, gids, runtime_flags, rlimits, mount_external, fas_samsung_p = ForkApp(
se_info, Anon(jint), Anon(jint), nice_name, fds_to_close, fds_to_ignore, is_child_zygote, "samsung_p",
instruction_set, app_data_dir]) [
uid,
gid,
gids,
runtime_flags,
rlimits,
mount_external,
se_info,
Anon(jint),
Anon(jint),
nice_name,
fds_to_close,
fds_to_ignore,
is_child_zygote,
instruction_set,
app_data_dir,
],
)
spec_q = SpecApp('q', [uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, spec_q = SpecializeApp(
nice_name, is_child_zygote, instruction_set, app_data_dir]) "q",
[
uid,
gid,
gids,
runtime_flags,
rlimits,
mount_external,
se_info,
nice_name,
is_child_zygote,
instruction_set,
app_data_dir,
],
)
spec_q_alt = SpecApp('q_alt', [uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, spec_q_alt = SpecializeApp(
nice_name, is_child_zygote, instruction_set, app_data_dir, is_top_app]) "q_alt",
[
uid,
gid,
gids,
runtime_flags,
rlimits,
mount_external,
se_info,
nice_name,
is_child_zygote,
instruction_set,
app_data_dir,
is_top_app,
],
)
spec_r = SpecApp('r', [uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, spec_r = SpecializeApp(
is_child_zygote, instruction_set, app_data_dir, is_top_app, pkg_data_info_list, "r",
whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs]) [
uid,
gid,
gids,
runtime_flags,
rlimits,
mount_external,
se_info,
nice_name,
is_child_zygote,
instruction_set,
app_data_dir,
is_top_app,
pkg_data_info_list,
whitelisted_data_info_list,
mount_data_dirs,
mount_storage_dirs,
],
)
spec_u = SpecApp('u', [uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, spec_u = SpecializeApp(
is_child_zygote, instruction_set, app_data_dir, is_top_app, pkg_data_info_list, "u",
whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs, mount_sysprop_overrides]) [
uid,
gid,
gids,
runtime_flags,
rlimits,
mount_external,
se_info,
nice_name,
is_child_zygote,
instruction_set,
app_data_dir,
is_top_app,
pkg_data_info_list,
whitelisted_data_info_list,
mount_data_dirs,
mount_storage_dirs,
mount_sysprop_overrides,
],
)
spec_samsung_q = SpecApp('samsung_q', [uid, gid, gids, runtime_flags, rlimits, mount_external, spec_samsung_q = SpecializeApp(
se_info, Anon(jint), Anon(jint), nice_name, is_child_zygote, instruction_set, app_data_dir]) "samsung_q",
[
uid,
gid,
gids,
runtime_flags,
rlimits,
mount_external,
se_info,
Anon(jint),
Anon(jint),
nice_name,
is_child_zygote,
instruction_set,
app_data_dir,
],
)
server_l = ForkServer('l', [uid, gid, gids, runtime_flags, rlimits, server_l = ForkServer(
permitted_capabilities, effective_capabilities]) "l",
[
uid,
gid,
gids,
runtime_flags,
rlimits,
permitted_capabilities,
effective_capabilities,
],
)
server_samsung_q = ForkServer('samsung_q', [uid, gid, gids, runtime_flags, Anon(jint), Anon(jint), rlimits, server_samsung_q = ForkServer(
permitted_capabilities, effective_capabilities]) "samsung_q",
[
uid,
gid,
gids,
runtime_flags,
Anon(jint),
Anon(jint),
rlimits,
permitted_capabilities,
effective_capabilities,
],
)
def gen_jni_def(field, methods):
decl = '' def gen_jni_def(field: str, methods: list[JNIHook]):
decl += ind(0) + f'std::array<JNINativeMethod, {len(methods)}> {field} = {{{{' decl = ""
decl += ind(0) + f"std::array<JNINativeMethod, {len(methods)}> {field} = {{{{"
for i, m in enumerate(methods): for i, m in enumerate(methods):
decl += ind(1) + f'// {m.name}' decl += ind(1) + f"// {m.name}"
decl += ind(1) + '{' decl += ind(1) + "{"
decl += ind(2) + f'"{m.base_name()}",' decl += ind(2) + f'"{m.hook_target()}",'
decl += ind(2) + f'"{m.jni()}",' decl += ind(2) + f'"{m.jni_sig()}",'
decl += ind(2) + f'(void *) +[] [[clang::no_stack_protector]] (JNIEnv *env, jclass clazz, {m.cpp()}) static -> {m.ret.type.cpp} {{' decl += ind(2) + f"(void *) +{m.cpp_lambda_sig()} {{"
decl += m.body(field, i) orig_fn_ptr = f"g_hook->{field}[{i}].fnPtr"
if m.ret.value: decl += m.body(orig_fn_ptr)
decl += ind(3) + f'return {m.ret.value};' decl += ind(2) + "}"
decl += ind(2) + '}' decl += ind(1) + "},"
decl += ind(1) + '},' decl += ind(0) + "}};"
decl += ind(0) + '}};'
decl += ind(0) decl += ind(0)
return decl return decl
with open('jni_hooks.hpp', 'w') as f:
f.write('// Generated by gen_jni_hooks.py\n')
f.write(gen_jni_def('fork_app_methods', [ with open("jni_hooks.hpp", "w") as f:
fas_l, fas_o, fas_p, fas_q_alt, fas_r, fas_u, fas_b, fas_samsung_m, fas_samsung_n, fas_samsung_o, fas_samsung_p])) f.write("// Generated by gen_jni_hooks.py\n")
f.write(gen_jni_def('specialize_app_methods', [spec_q, spec_q_alt, spec_r, spec_u, spec_samsung_q])) f.write(
gen_jni_def(
"fork_app_methods",
[
fas_l,
fas_o,
fas_p,
fas_q_alt,
fas_r,
fas_u,
fas_b,
fas_samsung_m,
fas_samsung_n,
fas_samsung_o,
fas_samsung_p,
],
)
)
f.write(gen_jni_def('fork_server_methods', [server_l, server_samsung_q])) f.write(
gen_jni_def(
"specialize_app_methods",
[spec_q, spec_q_alt, spec_r, spec_u, spec_samsung_q],
)
)
f.write('\n') f.write(gen_jni_def("fork_server_methods", [server_l, server_samsung_q]))
f.write("\n")