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