magisk 版本:https://github.com/topjohnwu/Magisk/tree/366dd524197d207be76322cb4f45044f9b466e93
Magisk开启zygisk 最终会进入mount_zygisk
0x1 mount_zygisk
#
define
mount_zygisk(bit)
if
(access(
"/system/bin/app_process"
#bit, F_OK) == 0) {
app_process_##bit = xopen(
"/system/bin/app_process"
#bit, O_RDONLY | O_CLOEXEC);
string zbin = zygisk_bin +
"/app_process"
#bit;
string mbin = MAGISKTMP +
"/magisk"
#bit;
int src = xopen(mbin.data(), O_RDONLY | O_CLOEXEC);
int out = xopen(zbin.data(), O_CREAT | O_WRONLY | O_CLOEXEC, 0);
xsendfile(out, src, nullptr, INT_MAX);
close(out);
close(src);
clone_attr(
"/system/bin/app_process"
#bit, zbin.data());
bind_mount(
"zygisk"
, zbin.data(),
"/system/bin/app_process"
#bit);
}
主要做了以下工作
-
保存应位数的/system/bin/app_process文件 ("/system/bin/app_process" #bit) -
将 /sbin/magisk(32/64) 复制到 /sbin/.magisk/zygisk/app_process(32/64) (android 11 以下是 /sbin,11之上在/dev下随机创建一个文件夹) -
将系统 /system/bin/app_process(32/64) 的属性复制给/sbin/.magisk/zygisk/app_process(32/64) -
最后将 /sbin/.magisk/zygisk/app_process(32/64) 挂载到 /system/bin/app_process(32/64) -
执行 /system/bin/app_process(32/64) 就是 执行 /sbin/.magisk/zygisk/app_process(32/64)
最终/system/bin 下的 app_process 就变成了 magisk ,而原先的 app_process 的 fd 被 magiskd持有。执行 app_process 的时候就是执行了 magisk 。
0x2 magisk 的 main
// native/src/core/applets.cpp
int
main
(
int
argc,
char
*argv[])
{
if
(argc <
1
)
return
1
;
enable_selinux();
cmdline_logging();
init_argv0(argc, argv);
string_view argv0 = basename(argv[
0
]);
// app_process is actually not an applet
if
(argv0.starts_with(
"app_process"
)) {
return
app_process_main(argc, argv);
// 入口
}
// ...
return
1
;
}
2-1 app_process_main
// Magisk/native/src/zygisk/main.cpp
// Entrypoint for app_process overlay
int
app_process_main
(
int
argc,
char
*argv[])
{
android_logging();
char
buf[PATH_MAX];
//...
if
(
int
socket = zygisk_request(ZygiskRequest::SETUP); socket >=
0
) {
// 和 magisk 进行通信 通信成功
do
{
if
(read_int(socket) !=
0
)
break
;
// Send over zygisk loader
// zygisk_ld 在 native/out/generated/arm64-v8a_binaries.h
write_int(socket,
sizeof
(zygisk_ld));
xwrite(socket, zygisk_ld,
sizeof
(zygisk_ld));
// zygisk_ld 来自python 脚本组装的, 保存内容是 libzygisk-ld.so -》 zygisk/loader.c -> zygisk_inject_entry 注入入口
int
app_proc_fd = recv_fd(socket);
// 接收 系统的/system/bin/app_process(32/64) 的文件符
if
(app_proc_fd <
0
)
break
;
string
tmp = read_string(socket);
// 获取 MAGISKTMP 路径
if
(
char
*ld = getenv(
"LD_PRELOAD"
)) {
string
env = ld;
env +=
':'
;
env += HIJACK_BIN;
setenv(
"LD_PRELOAD"
, env.data(),
1
);
// 设置 LD_PRELOAD 是已经被magisk接收并挂载到HIJACK_BIN上的 zygisk_ld.so
}
else
{
setenv(
"LD_PRELOAD"
, HIJACK_BIN,
1
);
// 设置 LD_PRELOAD 预加载so 启动进程前设置LD_PRELOAD变量(https://blog.csdn.net/whatday/article/details/108890018)
}
setenv(MAGISKTMP_ENV, tmp.data(),
1
);
close(socket);
// 执行一个新程序,同时确保在执行过程中关闭了不需要的文件描述符(FD_CLOEXEC)
fcntl(app_proc_fd, F_SETFD, FD_CLOEXEC);
fexecve(app_proc_fd, argv, environ);
// environ 是全局变量 设置了LD_PRELOAD 会预先脚在 环境变量设置的so
}
while
(
false
);
close(socket);
}
// If encountering any errors, unmount and execute the original app_process
xreadlink(
"/proc/self/exe"
, buf,
sizeof
(buf));
xumount2(
"/proc/self/exe"
, MNT_DETACH);
execve(buf, argv, environ);
return
1
;
}
2-2 zygisk 的处理函数zygisk_handler(client, &cred)
zygisk_handler 有多个信号处理对应的函数
void
zygisk_handler
(
int
client,
const
sock_cred *cred)
{
int
code = read_int(client);
char
buf[
256
];
switch
(code) {
case
ZygiskRequest::SETUP:
setup_files(client, cred);
// 处理 SETUP 信号
break
;
case
ZygiskRequest::PASSTHROUGH:
magiskd_passthrough(client);
break
;
case
ZygiskRequest::GET_INFO:
get_process_info(client, cred);
break
;
case
ZygiskRequest::GET_LOG_PIPE:
send_log_pipe(client);
break
;
case
ZygiskRequest::CONNECT_COMPANION:
if
(get_exe(cred->pid, buf,
sizeof
(buf))) {
connect_companion(client, str_ends(buf,
"64"
));
}
else
{
LOGW(
"zygisk: remote process %d probably died, abortn"
, cred->pid);
}
break
;
case
ZygiskRequest::GET_MODDIR:
get_moddir(client);
break
;
default
:
// Unknown code
break
;
}
close(client);
}
2-3 setup_files
// #define HIJACK_BIN64 "/system/bin/appwidget"
// #define HIJACK_BIN32 "/system/bin/bu"
// #define SEPOL_FILE_TYPE "magisk_file"
// native/src/zygisk/entry.cpp
static
void
setup_files
(
int
client,
const
sock_cred *cred)
{
LOGD(
"zygisk: setup files for pid=[%d]n"
, cred->pid);
// ...
// Hijack some binary in /system/bin to host loader
const
char
*hbin;
string
mbin;
int
app_fd;
bool
is_64_bit = str_ends(buf,
"64"
);
if
(is_64_bit) {
hbin = HIJACK_BIN64;
mbin = MAGISKTMP +
"/"
ZYGISKBIN
"/loader64.so"
;
app_fd = app_process_64;
}
else
{
hbin = HIJACK_BIN32;
mbin = MAGISKTMP +
"/"
ZYGISKBIN
"/loader32.so"
;
app_fd = app_process_32;
}
// ...
// Ack
write_int(client,
0
);
// 写入0 继续执行
// Receive and bind mount loader // 接受传过来的二进制(libzygisk-ld.so)
int
ld_fd = xopen(mbin.data(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC,
0755
);
string
ld_data = read_string(client);
// 先读取大小再读取内容
xwrite(ld_fd, ld_data.data(), ld_data.size());
// 写入mbin文件(/sbin/.magisk/zygisk/loader64.so)内
close(ld_fd);
// 设置文件的安全上下文(SELinux 上下文)
setfilecon(mbin.data(),
"u:object_r:"
SEPOL_FILE_TYPE
":s0"
);
xmount(mbin.data(), hbin,
nullptr
, MS_BIND,
nullptr
);
// mbin(/sbin/.magisk/zygisk/loader64.so) 文件挂在到hbin(/system/bin/appwidget)文件上
send_fd(client, app_fd);
// 发送系统原来的 /system/bin/app_process(32/64) 文件符号
write_string(client, MAGISKTMP);
// 写入 路径"/sbin"
}
完成在/system/bin/app_process zygote 上注入在 libzygisk-ld.so
0x3 注入zygisk流程图
接下来分析libzygisk-ld.so做了些什么
0x4 llibzygisk-ld.so 的初始化函数
根据编译文件定位文件loader.c
LOCAL_MODULE := zygisk-ld
LOCAL_SRC_FILES := zygisk/loader.c
// native/src/zygisk/loader.c
#
if
defined(__LP64__)
// Use symlink to workaround linker bug on old broken Android
// https://issuetracker.google.com/issues/36914295
#
define
SECOND_STAGE_PATH
"/system/bin/app_process"
#
else
#
define
SECOND_STAGE_PATH
"/system/bin/app_process32"
#
endif
__attribute__((constructor))
static
void
zygisk_loader
()
{
android_dlextinfo info = {
.flags = ANDROID_DLEXT_FORCE_LOAD
};
// Android 5.x doesn't support ANDROID_DLEXT_FORCE_LOAD
void
*handle =
android_dlopen_ext(SECOND_STAGE_PATH, RTLD_LAZY, &info) ?:
dlopen(SECOND_STAGE_PATH, RTLD_LAZY);
if
(handle) {
void
(*entry)(
void
*) = dlsym(handle,
"zygisk_inject_entry"
);
// 找到符号zygisk_inject_entry
if
(entry) {
entry(handle);
// 执行 zygisk_inject_entry 方法
}
}
}
加载/system/bin/app_process
(已经被替换为magisk)找到符号zygisk_inject_entr
并执行。
4-1 zygisk_inject_entr
// native/src/zygisk/entry.cpp
extern
"C"
void
zygisk_inject_entry
(
void
*handle)
{
// 注入入口
zygisk_logging();
ZLOGD(
"load successn"
);
// 如果包含多个路径,代码会截取第一个路径,并将其设置回 LD_PRELOAD 环境变量中。如果只有一个路径或没有路径,它会将 LD_PRELOAD 环境变量删除。
char
*ld = getenv(
"LD_PRELOAD"
);
if
(
char
*c =
strrchr
(ld,
':'
)) {
*c =
'�'
;
setenv(
"LD_PRELOAD"
, ld,
1
);
// Restore original LD_PRELOAD
}
else
{
unsetenv(
"LD_PRELOAD"
);
}
MAGISKTMP = getenv(MAGISKTMP_ENV);
self_handle = handle;
unsetenv(MAGISKTMP_ENV);
sanitize_environ();
// 让环境变量对齐,避免检测到被删除的环境变量留下的空白
hook_functions();
// 核心函数
new_daemon_thread(&unload_first_stage,
nullptr
);
}
4-2 hook_functions
这部分才是 zygisk 的核心 plt hook fork unshare 等函数 androidSetCreateThreadFunc
(重点函数)
// native/src/zygisk/hook.cpp
hash_map<xstring, tree_map<xstring, tree_map<xstring,
void
*>>> *jni_method_map;
// template<class T>
// static inline void default_new(T *&p) { p = new T(); }
// template<class T>
// static inline void default_new(std::unique_ptr<T> &p) { p.reset(new T()); }
void
hook_functions
()
{
default_new(plt_hook_list);
default_new(jni_hook_list);
default_new(jni_method_map);
ino_t
android_runtime_inode =
0
;
dev_t
android_runtime_dev =
0
;
for
(
auto
&
map
: lsplt::MapInfo::Scan()) {
if
(
map
.path.ends_with(
"libandroid_runtime.so"
)) {
android_runtime_inode =
map
.inode;
android_runtime_dev =
map
.dev;
break
;
}
}
PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, fork);
PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, unshare);
PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, selinux_android_setcontext);
PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, androidSetCreateThreadFunc);
PLT_HOOK_REGISTER_SYM(android_runtime_dev, android_runtime_inode,
"__android_log_close"
, android_log_close);
hook_commit();
// Remove unhooked methods
plt_hook_list->erase(
std
::remove_if(plt_hook_list->begin(), plt_hook_list->end(),
[](
auto
&t) {
return
*
std
::get<
3
>(t) ==
nullptr
;}),
plt_hook_list->end());
}
首先看几个定义的宏,最终通过lsplt::RegisterHook进行hook,发现 hook_register symbol 和 new_func 是拼接的,如androidSetCreateThreadFunc 该位置是
(void **) &old_androidSetCreateThreadFunc
(void*)new_androidSetCreateThreadFunc
// native/src/zygisk/hook.cpp
static
void
hook_register
(
dev_t
dev,
ino_t
inode,
const
char
*symbol,
void
*new_func,
void
**old_func)
{
if
(!lsplt::RegisterHook(dev, inode, symbol, new_func, old_func)) {
ZLOGE(
"Failed to register plt_hook "%s"n"
, symbol);
return
;
}
plt_hook_list->emplace_back(dev, inode, symbol, old_func);
}
#
define
PLT_HOOK_REGISTER_SYM(DEV, INODE, SYM, NAME)
hook_register(DEV, INODE, SYM, (void*) new_##NAME, (void **) &old_##NAME)
#
define
PLT_HOOK_REGISTER(DEV, INODE, NAME)
PLT_HOOK_REGISTER_SYM(DEV, INODE, #NAME, NAME)
4-3 跟踪androidSetCreateThreadFunc
定位函数指针定义的位置,找到了一个DCL_HOOK_FUNC
宏是用于声明 hook 函数和备份函数的。
// native/src/zygisk/hook.cpp
// Current context
HookContext *g_ctx;
const
JNINativeInterface *old_functions =
nullptr
;
JNINativeInterface *new_functions =
nullptr
;
}
// namespace
#
define
DCL_HOOK_FUNC(ret, func, ...)
ret (*old_##func)(__VA_ARGS__);
ret new_##func(__VA_ARGS__)
jint
env_RegisterNatives
(
JNIEnv *env, jclass clazz,
const
JNINativeMethod *methods, jint numMethods)
{
auto
className = get_class_name(env, clazz);
ZLOGV(
"JNIEnv->RegisterNatives [%s]n"
, className.data());
// 调用hookAndSaveJNIMethods
auto
newMethods = hookAndSaveJNIMethods(className.data(), methods, numMethods);
// 调用原来的jni方法 有newMethods 就使用newMethods(替换后的函数)
return
old_functions->RegisterNatives(env, clazz, newMethods.get() ?: methods, numMethods);
}
DCL_HOOK_FUNC(
void
, androidSetCreateThreadFunc,
void
*func) {
//new_androidSetCreateThreadFunc 函数体
ZLOGD(
"androidSetCreateThreadFuncn"
);
using
method_sig = jint(*)(JavaVM **, jsize, jsize *);
do
{
auto
get_created_vms =
reinterpret_cast
<method_sig>(
dlsym(RTLD_DEFAULT,
"JNI_GetCreatedJavaVMs"
));
// RTLD_DEFAULT 是一个特殊的句柄,用于指示 dlsym 在所有已加载的共享库中搜索符号
if
(!get_created_vms) {
for
(
auto
&
map
: lsplt::MapInfo::Scan()) {
if
(!
map
.path.ends_with(
"/libnativehelper.so"
))
continue
;
void
*h = dlopen(
map
.path.data(), RTLD_LAZY);
if
(!h) {
LOGW(
"cannot dlopen libnativehelper.so: %sn"
, dlerror());
break
;
}
// 从 libnativehelper.so 获取JNI_GetCreatedJavaVMs 方法
get_created_vms =
reinterpret_cast
<method_sig>(dlsym(h,
"JNI_GetCreatedJavaVMs"
));
dlclose(h);
break
;
}
if
(!get_created_vms) {
LOGW(
"JNI_GetCreatedJavaVMs not foundn"
);
break
;
}
}
JavaVM *vm =
nullptr
;
jsize num =
0
;
jint res = get_created_vms(&vm,
1
, &num);
if
(res != JNI_OK || vm ==
nullptr
)
break
;
JNIEnv *env =
nullptr
;
res = vm->GetEnv(
reinterpret_cast
<
void
**>(&env), JNI_VERSION_1_6);
if
(res != JNI_OK || env ==
nullptr
)
break
;
default_new(new_functions);
// new一个JNINativeInterface空函数
memcpy
(new_functions, env->functions,
sizeof
(*new_functions));
// 复制env->functions
new_functions->RegisterNatives = &env_RegisterNatives;
// 替换RegisterNatives方法
// Replace the function table in JNIEnv to hook RegisterNatives
old_functions = env->functions;
// 将原来的jni函数进行保存
env->functions = new_functions;
// 替换env->functions 修改了RegisterNatives
}
while
(
false
);
old_androidSetCreateThreadFunc(func);
// 执行原来的 androidSetCreateThreadFunc 函数
}
-
当系统调用
androidSetCreateThreadFunc
方法时先替换jni
中的RegisterNatives
方法再执行原来的androidSetCreateThreadFunc
方法 -
执行当
jni
执行RegisterNatives
会先执行hookAndSaveJNIMethods
再执行原的RegisterNatives
-
为什么选择
hookRegisterNatives
,是因为zyogte
启动过程过程中会调用androidSetCreateThreadFunc->RegisterNatives
,时机比较早
4-4 hookAndSaveJNIMethods
-
只有 className 是 com/android/internal/os/Zygote才进行hook
// native/src/zygisk/jni_hooks.hpp
unique_ptr
<JNINativeMethod[]>
hookAndSaveJNIMethods
(
const
char
*className,
const
JNINativeMethod *methods,
int
numMethods)
{
unique_ptr
<JNINativeMethod[]> newMethods;
int
clz_id =
-1
;
int
hook_cnt =
0
;
do
{
if
(className ==
"com/android/internal/os/Zygote"
sv) {
// "com/android/internal/os/Zygote" 是一个 Android 系统中的类,它是 Android 启动过程中的第一个特定于 Android 的进程,它负责预加载所有系统资源和类,并为每个应用程序创建新的进程
clz_id =
0
;
hook_cnt =
3
;
break
;
}
}
while
(
false
);
if
(hook_cnt) {
newMethods = make_unique<JNINativeMethod[]>(numMethods);
memcpy
(newMethods.get(), methods,
sizeof
(JNINativeMethod) * numMethods);
}
auto
&class_map = (*jni_method_map)[className];
// 返回这个新创建的 tree_map 的引用
for
(
int
i =
0
; i < numMethods; ++i) {
if
(hook_cnt && clz_id ==
0
) {
// 重点hook的三个函数
HOOK_JNI(nativeForkAndSpecialize)
HOOK_JNI(nativeSpecializeAppProcess)
HOOK_JNI(nativeForkSystemServer)
}
class_map[methods[i].name][methods[i].signature] = methods[i].fnPtr;
}
return
newMethods;
}
4-4 HOOK_JNI
// 用zygisk的方法(method##_methods[j])替换, 之后执行的方法就是zygisk中定义的函数
// method##_orig = methods[i].fnPtr; 保存原来的函数指针
// native/src/zygisk/hook.cpp
#
define
HOOK_JNI(method)
if
(methods[i].name == #method##sv) {
int j = 0;
for (; j < method##_methods_num; ++j) {
if
(strcmp(methods[i].signature, method##_methods[j].signature) == 0) {
jni_hook_list->try_emplace(className).first->second.push_back(methods[i]);
method##_orig = methods[i].fnPtr;
newMethods[i] = method##_methods[j];
ZLOGI(
"replaced %s#"
#method
"n"
, className);
--hook_cnt;
break;
}
}
if
(j == method##_methods_num) {
ZLOGE(
"unknown signature of %s#"
#method
": %sn"
, className, methods[i].signature);
}
continue;
}
三个函数的逻辑基本一致,这里挑选一个函数进行跟踪
nativeForkSystemServer
// native/src/zygisk/jni_hooks.hpp
// 收集了android不同版本同一个函数不同的签名 比如 nativeForkSystemServer
// newMethods[i] = method##_methods[j]; => newMethods[i] = nativeForkSystemServer_methods[j]; 替换函数
const
JNINativeMethod nativeForkSystemServer_methods[] = {
{
"nativeForkSystemServer"
,
"(II[II[[IJJ)I"
,
(
void
*) &nativeForkSystemServer_l
},
{
"nativeForkSystemServer"
,
"(II[IIII[[IJJ)I"
,
(
void
*) &nativeForkSystemServer_samsung_q
},
};
constexpr
int
nativeForkSystemServer_methods_num =
std
::size(nativeForkSystemServer_methods);
void
*nativeForkSystemServer_orig =
nullptr
;
[[clang::no_stack_protector]]
jint
nativeForkSystemServer_l
(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jlong permitted_capabilities, jlong effective_capabilities)
{
ServerSpecializeArgs_v1
args
(uid, gid, gids, runtime_flags, permitted_capabilities, effective_capabilities)
;
HookContext ctx;
ctx.env = env;
ctx.args = { &args };
ctx.nativeForkSystemServer_pre();
// nativeForkSystemServer 执行前
reinterpret_cast
<
decltype
(&nativeForkSystemServer_l)>(nativeForkSystemServer_orig)(
env, clazz, uid, gid, gids, runtime_flags, rlimits, permitted_capabilities, effective_capabilities
);
// 执行原函数
ctx.nativeForkSystemServer_post();
// nativeForkSystemServer 执行后
return
ctx.pid;
}
nativeForkSystemServer_pre
void
HookContext::nativeForkSystemServer_pre
()
{
ZLOGV(
"pre forkSystemServern"
);
flags[SERVER_FORK_AND_SPECIALIZE] =
true
;
fork_pre();
if
(pid !=
0
)
return
;
vector
<
int
> module_fds;
int
fd = remote_get_info(
1000
,
"system_server"
, &info_flags, module_fds);
if
(fd >=
0
) {
if
(module_fds.empty()) {
write_int(fd,
0
);
}
else
{
run_modules_pre(module_fds);
// 运行modules插入的hook代码
// Send the bitset of module status back to magiskd from system_server
dynamic_bitset bits;
for
(
const
auto
&m : modules)
bits[m.getId()] =
true
;
write_int(fd,
static_cast
<
int
>(bits.slots()));
for
(
int
i =
0
; i < bits.slots(); ++i) {
auto
l = bits.get_slot(i);
xwrite(fd, &l,
sizeof
(l));
}
}
close(fd);
}
sanitize_fds();
}
void
HookContext::nativeForkSystemServer_post
()
{
if
(pid ==
0
) {
ZLOGV(
"post forkSystemServern"
);
run_modules_post();
}
fork_post();
}
-
Zygisk 加载是通过替换 app_process ,修改 LD_PRELOAD ,再执行原 app_process 实现 -
LD_PRELOAD 执行的so,hook三个和创建进程开辟进程空间有关系的关键函数
参考文章:
https://gist.github.com/5ec1cff/bfe06429f5bf1da262c40d0145e9f190#file-zygisk-md
原文始发于微信公众号(逆向与采集):android|Magisk注入Zygisk的过程
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论