0%

Magiskinit学习

Magisk文档

https://topjohnwu.github.io/Magisk/details.html
image.png

2SI是先执行第一阶段,再执行第二阶段,这里的执行顺序是:

  • magiskinit 第一阶段
  • /system/bin/init 第一阶段,这里执行完成本来是要执行/system/bin/init第二阶段的,但是被修改为了执行magiskinit
  • magiskinit 第二阶段
  • /system/bin/init 第二阶段

Magisk init 第一阶段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int main(int argc, char *argv[]) {
...
load_kernel_info(&config);
if (config.skip_initramfs)
init = new LegacySARInit(argv, &config);
else if (config.force_normal_boot)
init = new FirstStageInit(argv, &config);
else if (access("/sbin/recovery", F_OK) == 0 || access("/system/bin/recovery", F_OK) == 0)
init = new RecoveryInit(argv, &config);
else if (check_two_stage())
init = new FirstStageInit(argv, &config);
else
init = new RootFSInit(argv, &config);
// Run the main routine
init->start();
exit(1);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
void load_kernel_info(BootConfig *config) {
// Get kernel data using procfs and sysfs
xmkdir("/proc", 0755);
xmount("proc", "/proc", "proc", 0, nullptr);
xmkdir("/sys", 0755);
xmount("sysfs", "/sys", "sysfs", 0, nullptr);

mount_list.emplace_back("/proc");
mount_list.emplace_back("/sys");

// Log to kernel
rust::setup_klog();

config->set(parse_cmdline(full_read("/proc/cmdline"))); // 内核启动时的参数
config->set(parse_bootconfig(full_read("/proc/bootconfig")));

parse_prop_file("/.backup/.magisk", [=](auto key, auto value) -> bool {
if (key == "RECOVERYMODE" && value == "true") { // 解析备份的.magisk
config->skip_initramfs = config->emulator || !check_key_combo();
return false;
}
return true;
});

if (config->dt_dir[0] == '\0')
strscpy(config->dt_dir, DEFAULT_DT_DIR, sizeof(config->dt_dir));

char file_name[128];
read_dt("fstab_suffix", fstab_suffix)
read_dt("hardware", hardware)
read_dt("hardware.platform", hardware_plat)

LOGD("Device config:\n");
config->print();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
class FirstStageInit : public BaseInit {
private:
void prepare();
public:
FirstStageInit(char *argv[], BootConfig *config) : BaseInit(argv, config) {
LOGD("%s\n", __FUNCTION__);
};
void start() override {
prepare(); // 挂载并劫持原init
exec_init(); // 执行原init第一阶段
}
};

void FirstStageInit::prepare() {
prepare_data();
restore_ramdisk_init(); // 将原本的system/bin/init 恢复到/init
auto init = mmap_data("/init", true); //将原本init读入到内存
// Redirect original init to magiskinit
// // 将字符串/system/bin/init 替换为 /data/magiskinit 主要是为了劫持第二阶段
for (size_t off : init.patch(INIT_PATH, REDIR_PATH)) {
LOGD("Patch @ %08zX [" INIT_PATH "] -> [" REDIR_PATH "]\n", off);
}
}

void BaseInit::prepare_data() {
LOGD("Setup data tmp\n");
xmkdir("/data", 0755); //创建 data目录
xmount("magisk", "/data", "tmpfs", 0, "mode=755"); // 将magisk挂载到data目录,文件系统类型为tmpfs
// 以下3个都是patch cpio 修改的,这里进行复制
cp_afc("/init", "/data/magiskinit");
cp_afc("/.backup", "/data/.backup");
cp_afc("/overlay.d", "/data/overlay.d");
}

void restore_ramdisk_init() {
unlink("/init");

const char *orig_init = backup_init(); // 这里返回原init的地址
if (access(orig_init, F_OK) == 0) {
xrename(orig_init, "/init"); // 将/init 替换为 原init
} else {
// If the backup init is missing, this means that the boot ramdisk
// was created from scratch, and the real init is in a separate CPIO,
// which is guaranteed to be placed at /system/bin/init.
xsymlink(INIT_PATH, "/init");
}
}
void BaseInit::exec_init() {
// Unmount in reverse order
for (auto &p : reversed(mount_list)) { // mount_list 是proc和sys,这里是因为提前mount了,需要unmount
if (xumount2(p.data(), MNT_DETACH) == 0)
LOGD("Unmount [%s]\n", p.data());
}
execv("/init", argv);
exit(1);
}

原init第一阶段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int main(int argc, char** argv) {
...
if (argc > 1) {
if (!strcmp(argv[1], "subcontext")) {
android::base::InitLogging(argv, &android::base::KernelLogger);
const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();

return SubcontextMain(argc, argv, &function_map);
}

if (!strcmp(argv[1], "selinux_setup")) {
return SetupSelinux(argv);
}

if (!strcmp(argv[1], "second_stage")) {
return SecondStageMain(argc, argv);
}
}
// 执行这里
return FirstStageMain(argc, argv);
}
1
2
3
4
5
6
7
8
9
10
11
12
int FirstStageMain(int argc, char** argv) {
... // 挂载文件等操作
const char* path = "/system/bin/init"; // 这里的path已经被替换为了/data/magiskinit
const char* args[] = {path, "selinux_setup", nullptr};
auto fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
close(fd);
execv(path, const_cast<char**>(args)); // 执行

return 1;
}

Magisk init 第二阶段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
int main(int argc, char *argv[]) {
if (argc > 1 && argv[1] == "selinux_setup"sv) {
init = new SecondStageInit(argv); // 执行第二阶段
}
init->start();
}


class SecondStageInit : public MagiskInit {
private:
bool prepare();
public:
SecondStageInit(char *argv[]) : MagiskInit(argv) {
LOGD("%s\n", __FUNCTION__);
};

void start() override {
bool is_rootfs = prepare();
if (is_rootfs)
patch_rw_root();
else
patch_ro_root();
exec_init();
}
};


bool SecondStageInit::prepare() {
umount2("/init", MNT_DETACH);
unlink("/data/init");

// Make sure init dmesg logs won't get messed up
argv[0] = (char *) INIT_PATH; // argv[0] = /system/bin/init

// Some weird devices like meizu, uses 2SI but still have legacy rootfs
struct statfs sfs{};
statfs("/", &sfs);
if (sfs.f_type == RAMFS_MAGIC || sfs.f_type == TMPFS_MAGIC) {
// We are still on rootfs, so make sure we will execute the init of the 2nd stage
unlink("/init");
xsymlink(INIT_PATH, "/init"); //再次重新将/system/bin/init指向 /init
return true;
}
return false;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
void MagiskInit::patch_ro_root() {
mount_list.emplace_back("/data");
parse_config_file();

string tmp_dir;
// magisk资源目录,使用magisk --path 可以查看
if (access("/sbin", F_OK) == 0) {
tmp_dir = "/sbin";
} else {
tmp_dir = "/debug_ramdisk";
xmkdir("/data/debug_ramdisk", 0);
xmount("/debug_ramdisk", "/data/debug_ramdisk", nullptr, MS_MOVE, nullptr);
}
// 创建这个资源目录
setup_tmp(tmp_dir.data());
chdir(tmp_dir.data()); // 进入到资源目录

if (tmp_dir == "/sbin") {
// Recreate original sbin structure
xmkdir(ROOTMIR, 0755);
xmount("/", ROOTMIR, nullptr, MS_BIND, nullptr);
recreate_sbin(ROOTMIR "/sbin", true);
xumount2(ROOTMIR, MNT_DETACH);
} else {
// Restore debug_ramdisk
xmount("/data/debug_ramdisk", "/debug_ramdisk", nullptr, MS_MOVE, nullptr);
rmdir("/data/debug_ramdisk");
}

xrename("overlay.d", ROOTOVL);

extern bool avd_hack;
// Handle avd hack
if (avd_hack) {
int src = xopen("/init", O_RDONLY | O_CLOEXEC);
mmap_data init("/init");
// Force disable early mount on original init
for (size_t off : init.patch("android,fstab", "xxx")) {
LOGD("Patch @ %08zX [android,fstab] -> [xxx]\n", off);
}
int dest = xopen(ROOTOVL "/init", O_CREAT | O_WRONLY | O_CLOEXEC, 0);
xwrite(dest, init.buf(), init.sz());
fclone_attr(src, dest);
close(src);
close(dest);
}

load_overlay_rc(ROOTOVL);
if (access(ROOTOVL "/sbin", F_OK) == 0) {
// Move files in overlay.d/sbin into tmp_dir
mv_path(ROOTOVL "/sbin", ".");
}

// Patch init.rc
if (access(NEW_INITRC, F_OK) == 0) {
// Android 11's new init.rc
xmkdirs(dirname(ROOTOVL NEW_INITRC), 0755);
patch_init_rc(NEW_INITRC, ROOTOVL NEW_INITRC, tmp_dir.data());
} else {
patch_init_rc("/init.rc", ROOTOVL "/init.rc", tmp_dir.data());
}

// Extract magisk
extract_files(false);

// Oculus Go will use a special sepolicy if unlocked
if (access("/sepolicy.unlocked", F_OK) == 0) {
patch_sepolicy("/sepolicy.unlocked", ROOTOVL "/sepolicy.unlocked");
} else if ((access(SPLIT_PLAT_CIL, F_OK) != 0 && access("/sepolicy", F_OK) == 0) ||
!hijack_sepolicy()) {
patch_sepolicy("/sepolicy", ROOTOVL "/sepolicy");
}

// Mount rootdir
magic_mount(ROOTOVL);
int dest = xopen(ROOTMNT, O_WRONLY | O_CREAT, 0);
write(dest, magic_mount_list.data(), magic_mount_list.length());
close(dest);

chdir("/");
}

这里主要看下 patch_init_rc,因为Magisk文档说会给init.rc 注入代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
static void patch_init_rc(const char *src, const char *dest, const char *tmp_dir) {
FILE *rc = xfopen(dest, "we");
if (!rc) {
PLOGE("%s: open %s failed", __PRETTY_FUNCTION__, src);
return;
}
file_readline(src, [=](string_view line) -> bool {
// Do not start vaultkeeper
if (str_contains(line, "start vaultkeeper")) {
LOGD("Remove vaultkeeper\n");
return true;
}
// Do not run flash_recovery
if (str_starts(line, "service flash_recovery")) {
LOGD("Remove flash_recovery\n");
fprintf(rc, "service flash_recovery /system/bin/xxxxx\n");
return true;
}
// Samsung's persist.sys.zygote.early will cause Zygote to start before post-fs-data
if (str_starts(line, "on property:persist.sys.zygote.early=")) {
LOGD("Invalidate persist.sys.zygote.early\n");
fprintf(rc, "on property:persist.sys.zygote.early.xxxxx=true\n");
return true;
}
// Else just write the line
fprintf(rc, "%s", line.data());
return true;
});

fprintf(rc, "\n");

// Inject custom rc scripts
for (auto &script : rc_list) {
// Replace template arguments of rc scripts with dynamic paths
replace_all(script, "${MAGISKTMP}", tmp_dir);
fprintf(rc, "\n%s\n", script.data());
}
rc_list.clear();

// Inject Magisk rc scripts
LOGD("Inject magisk rc\n");
fprintf(rc, R"EOF(
on post-fs-data
start logd
exec %2$s 0 0 -- %1$s/magisk --post-fs-data

on property:vold.decrypt=trigger_restart_framework
exec %2$s 0 0 -- %1$s/magisk --service

on nonencrypted
exec %2$s 0 0 -- %1$s/magisk --service

on property:sys.boot_completed=1
exec %2$s 0 0 -- %1$s/magisk --boot-complete

on property:init.svc.zygote=restarting
exec %2$s 0 0 -- %1$s/magisk --zygote-restart

on property:init.svc.zygote=stopped
exec %2$s 0 0 -- %1$s/magisk --zygote-restart
)EOF", tmp_dir, MAGISK_PROC_CON);

fclose(rc);
clone_attr(src, dest);
}

这里主要注入监听来回调magisk。
magisk命令入口:Magisk/native/src/core/magisk.cpp

原init第二阶段

初始化 property 服务,解析执行 rc 配置文件。

Magisk命令

init.rc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
on post-fs-data
start logd
exec %2$s 0 0 -- %1$s/magisk --post-fs-data

on property:vold.decrypt=trigger_restart_framework
exec %2$s 0 0 -- %1$s/magisk --service

on nonencrypted
exec %2$s 0 0 -- %1$s/magisk --service

on property:sys.boot_completed=1
exec %2$s 0 0 -- %1$s/magisk --boot-complete

on property:init.svc.zygote=restarting
exec %2$s 0 0 -- %1$s/magisk --zygote-restart

on property:init.svc.zygote=stopped
exec %2$s 0 0 -- %1$s/magisk --zygote-restart

post-fs-data

1
2
3
4
5
6
7
8
int magisk_main(int argc, char *argv[]) {
if (argv[1] == "--post-fs-data"sv) {
int fd = connect_daemon(MainRequest::POST_FS_DATA, true);
struct pollfd pfd = { fd, POLLIN, 0 };
poll(&pfd, 1, 1000 * POST_FS_DATA_WAIT_TIME);
return 0;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int connect_daemon(int req, bool create) {
int fd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
sockaddr_un addr = {.sun_family = AF_LOCAL};
string tmp = find_magisk_tmp();
strcpy(addr.sun_path, (tmp + "/" MAIN_SOCKET).data()); //sbin/.magisk/socket

// 返回0表示成功,首次肯定是返回0,因为没有magiskd进程
if (connect(fd, (sockaddr *) &addr, sizeof(addr))) {
...
if (fork_dont_care() == 0) { //fork 守护进程
close(fd); //关闭守护进程fd
daemon_entry(); // 进入守护进程后会死循环
}
// 父进程再次尝试连接deamon进程
while (connect(fd, (sockaddr *) &addr, sizeof(addr)))
usleep(10000);
}
write_int(fd, req);
...
return fd;
}

这里比较绕,首次链接deamon进程需要先创建deamon进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
static void daemon_entry() {
android_logging();

// Block all signals
sigset_t block_set;
sigfillset(&block_set);
pthread_sigmask(SIG_SETMASK, &block_set, nullptr);

// Change process name
set_nice_name("magiskd"); // 设置进程名

...

fd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
sockaddr_un addr = {.sun_family = AF_LOCAL};
strcpy(addr.sun_path, (MAGISKTMP + "/" MAIN_SOCKET).data());
unlink(addr.sun_path);
if (xbind(fd, (sockaddr *) &addr, sizeof(addr)))
exit(1);
chmod(addr.sun_path, 0666);
setfilecon(addr.sun_path, MAGISK_FILE_CON);
xlisten(fd, 10);

default_new(poll_map);
default_new(poll_fds);
default_new(module_list);

// Register handler for main socket
pollfd main_socket_pfd = { fd, POLLIN, 0 };
register_poll(&main_socket_pfd, handle_request);

// Loop forever to listen for requests
poll_loop();
}

handle_request

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static void handle_request(pollfd *pfd) {

... //鉴权

if (code < MainRequest::_SYNC_BARRIER_) {
// START_DAEMON,CHECK_VERSION,CHECK_VERSION_CODE,STOP_DAEMON,
handle_request_sync(client, code);
goto done;
} else if (code < MainRequest::_STAGE_BARRIER_) {
// SUPERUSER,ZYGOTE_RESTART,DENYLIST,SQLITE_CMD,REMOVE_MODULES,ZYGISK,ZYGISK_PASSTHROUGH,
exec_task([=] { handle_request_async(client, code, cred); });
} else { // POST_FS_DATA,LATE_START,BOOT_COMPLETE,
exec_task([=] { boot_stage_handler(client, code); });
}
return;

done:
close(client);
}

handle_request_sync

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
static void handle_request_sync(int client, int code) {
switch (code) {
case MainRequest::CHECK_VERSION:
#if MAGISK_DEBUG
write_string(client, MAGISK_VERSION ":MAGISK:D");
#else
write_string(client, MAGISK_VERSION ":MAGISK:R");
#endif
break;
case MainRequest::CHECK_VERSION_CODE:
write_int(client, MAGISK_VER_CODE);
break;
case MainRequest::START_DAEMON:
rust::get_magiskd().setup_logfile();
break;
case MainRequest::STOP_DAEMON:
denylist_handler(-1, nullptr);
write_int(client, 0);
// Terminate the daemon!
exit(0);
default:
__builtin_unreachable();
}
}

handle_request_async

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
static void handle_request_async(int client, int code, const sock_cred &cred) {
switch (code) {
case MainRequest::DENYLIST:
denylist_handler(client, &cred);
break;
case MainRequest::SUPERUSER:
su_daemon_handler(client, &cred);
break;
case MainRequest::ZYGOTE_RESTART:
close(client);
LOGI("** zygote restarted\n");
pkg_xml_ino = 0;
prune_su_access();
break;
case MainRequest::SQLITE_CMD:
exec_sql(client);
break;
case MainRequest::REMOVE_MODULES: {
int do_reboot = read_int(client);
remove_modules();
write_int(client, 0);
close(client);
if (do_reboot) reboot();
break;
}
case MainRequest::ZYGISK:
case MainRequest::ZYGISK_PASSTHROUGH:
zygisk_handler(client, &cred);
break;
default:
__builtin_unreachable();
}
}

boot_stage_handler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void boot_stage_handler(int client, int code) {
// Make sure boot stage execution is always serialized
static pthread_mutex_t stage_lock = PTHREAD_MUTEX_INITIALIZER;
mutex_guard lock(stage_lock);

switch (code) {
case MainRequest::POST_FS_DATA:
if ((boot_state & FLAG_POST_FS_DATA_DONE) == 0)
post_fs_data();
close(client);
break;
case MainRequest::LATE_START:
close(client);
if ((boot_state & FLAG_POST_FS_DATA_DONE) && (boot_state & FLAG_SAFE_MODE) == 0)
late_start();
break;
case MainRequest::BOOT_COMPLETE:
close(client);
if ((boot_state & FLAG_SAFE_MODE) == 0)
boot_complete();
break;
default:
__builtin_unreachable();
}
}

image.png
image.png
这里关注下POST_FS_DATA

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
static void post_fs_data() {

mount_mirrors(); // 挂在mirror
prune_su_access();

if (access(SECURE_DIR, F_OK) != 0) {
LOGE(SECURE_DIR " is not present, abort\n");
goto early_abort;
}

if (!magisk_env()) {
LOGE("* Magisk environment incomplete, abort\n");
goto early_abort;
}

if (get_prop("persist.sys.safemode", true) == "1" ||
get_prop("ro.sys.safemode") == "1" || check_key_combo()) {
boot_state |= FLAG_SAFE_MODE;
// Disable all modules and denylist so next boot will be clean
disable_modules();
disable_deny();
} else {
exec_common_scripts("post-fs-data"); // 执行/data/adb/post-fs-data.d
db_settings dbs;
get_db_settings(dbs, ZYGISK_CONFIG); // 获取zygisk配置
zygisk_enabled = dbs[ZYGISK_CONFIG]; // 判断是否开启zygisk
initialize_denylist(); //
handle_modules(); // 校验modules,执行module的post-fs-data
}

early_abort:
// We still do magic mount because root itself might need it
load_modules(); //加载module
boot_state |= FLAG_POST_FS_DATA_DONE;
}

magisk_env

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
static bool magisk_env() {
char buf[4096];

LOGI("* Initializing Magisk environment\n");

preserve_stub_apk();
string pkg;
get_manager(0, &pkg);

// Directories in /data/adb
xmkdir(DATABIN, 0755);
xmkdir(SECURE_DIR "/post-fs-data.d", 0755);
xmkdir(SECURE_DIR "/service.d", 0755);
restorecon();

if (access(DATABIN "/busybox", X_OK))
return false;

sprintf(buf, "%s/" BBPATH "/busybox", MAGISKTMP.data());
mkdir(dirname(buf), 0755);
cp_afc(DATABIN "/busybox", buf);
exec_command_async(buf, "--install", "-s", dirname(buf));

if (access(DATABIN "/magiskpolicy", X_OK) == 0) {
sprintf(buf, "%s/magiskpolicy", MAGISKTMP.data());
cp_afc(DATABIN "/magiskpolicy", buf);
}

return true;
}

Zygisk

zygisk如果开启,会替换

1
2
3
4
5
6
7
8
9
10
11
12
13
#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
image.png

替换后的app_process 入口在Magisk/native/src/zygisk/main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
int app_process_main(int argc, char *argv[]) {

if (int socket = zygisk_request(ZygiskRequest::SETUP); socket >= 0) {
do {
if (read_int(socket) != 0)
break;

// Send over zygisk loader
write_int(socket, sizeof(zygisk_ld));
xwrite(socket, zygisk_ld, sizeof(zygisk_ld));

int app_proc_fd = recv_fd(socket); // 接收原本的app_process fd
if (app_proc_fd < 0)
break;

string tmp = read_string(socket);
if (char *ld = getenv("LD_PRELOAD")) {
string env = ld;
env += ':';
env += HIJACK_BIN;
setenv("LD_PRELOAD", env.data(), 1);
} else {
setenv("LD_PRELOAD", HIJACK_BIN, 1);
}
setenv(MAGISKTMP_ENV, tmp.data(), 1);

close(socket);

fcntl(app_proc_fd, F_SETFD, FD_CLOEXEC);
fexecve(app_proc_fd, argv, environ); // 启动原本的app_process
} 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;
}

image.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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, 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());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
DCL_HOOK_FUNC(void, androidSetCreateThreadFunc, void *func) {
ZLOGD("androidSetCreateThreadFunc\n");
hook_jni_env();
old_androidSetCreateThreadFunc(func);
}

// Skip actual fork and return cached result if applicable
DCL_HOOK_FUNC(int, fork) {
return (g_ctx && g_ctx->pid >= 0) ? g_ctx->pid : old_fork();
}

// Unmount stuffs in the process's private mount namespace
DCL_HOOK_FUNC(int, unshare, int flags) {
int res = old_unshare(flags);
if (g_ctx && (flags & CLONE_NEWNS) != 0 && res == 0 &&
// For some unknown reason, unmounting app_process in SysUI can break.
// This is reproducible on the official AVD running API 26 and 27.
// Simply avoid doing any unmounts for SysUI to avoid potential issues.
(g_ctx->info_flags & PROCESS_IS_SYS_UI) == 0) {
if (g_ctx->flags[DO_REVERT_UNMOUNT]) {
revert_unmount();
} else {
umount2("/system/bin/app_process64", MNT_DETACH);
umount2("/system/bin/app_process32", MNT_DETACH);
}
// Restore errno back to 0
errno = 0;
}
return res;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
static void hook_jni_env() {
using method_sig = jint(*)(JavaVM **, jsize, jsize *);
auto get_created_vms = reinterpret_cast<method_sig>(
dlsym(RTLD_DEFAULT, "JNI_GetCreatedJavaVMs"));
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) {
ZLOGW("Cannot dlopen libnativehelper.so: %s\n", dlerror());
break;
}
get_created_vms = reinterpret_cast<method_sig>(dlsym(h, "JNI_GetCreatedJavaVMs"));
dlclose(h);
break;
}
if (!get_created_vms) {
ZLOGW("JNI_GetCreatedJavaVMs not found\n");
return;
}
}

JavaVM *vm = nullptr;
jsize num = 0;
jint res = get_created_vms(&vm, 1, &num);
if (res != JNI_OK || vm == nullptr) {
ZLOGW("JavaVM not found\n");
return;
}
JNIEnv *env = nullptr;
res = vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
if (res != JNI_OK || env == nullptr) {
ZLOGW("JNIEnv not found\n");
return;
}

// Replace the function table in JNIEnv to hook RegisterNatives
default_new(new_functions);
memcpy(new_functions, env->functions, sizeof(*new_functions));
new_functions->RegisterNatives = &env_RegisterNatives;
old_functions = env->functions;
env->functions = new_functions;
}

其他

Android init Language Action

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Android的init语言(也称为init.rc)定义了启动过程中的一系列操作,称为Action。这些Actions用于在设备启动时执行各种任务和配置。

以下是Android Init语言中常见的一些Action:

Service(服务):启动和管理后台服务程序。
Write(写入):将指定的值写入到系统属性(System Property)中。
Symlink(符号链接):创建符号链接,将一个文件或目录链接到另一个位置。
Mount(挂载):挂载文件系统,将文件系统附加到指定的挂载点。
Setprop(设置属性):设置系统属性的值。
chmod(修改文件权限):更改指定文件或目录的权限。
chown(修改文件所有者):更改指定文件或目录的所有者。
exec(执行命令):执行指定的命令或脚本。
Import(导入):导入其他init脚本。
Wait(等待):等待指定的属性具有特定的值。
Trigger(触发):触发指定的事件。
这些Action是通过init.rc文件中的语法来定义和配置的。init.rc文件位于设备的根文件系统中,通常在/init.rc或者/system/etc/init目录中。它是在设备启动时由init进程解析和执行的。

配置和定义Action是Android系统初始化的关键部分,它们用于在设备启动过程中执行各种任务和操作,包括启动服务、设置属性、挂载文件系统等。通过修改和定制init.rc文件,可以实现在设备启动时进行个性化的配置和自定义操作。


Android init Language Trigger

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
以下是Android Init语言中可用的所有Trigger条件:

on early-init:在系统早期初始化阶段触发。
on init:在系统初始化阶段触发。
on vendor:在加载vendor分区之后触发。
on late-init:在系统启动完成后触发。
on property <property_name>:当指定系统属性的值变为非空时触发。
on property-change <property_name>:当指定系统属性的值发生变化时触发。
on property-exist <property_name>:当指定系统属性存在时触发。
on property-select <property_name> <property_value>:当指定系统属性的值等于给定值时触发。
on property-stop <property_name>:当指定系统属性的值停止时触发。
on boot:在系统引导过程中触发。
on uevent <uevent_action> <device_path>:当指定的设备事件发生时触发。
on fs <fs_type>:当指定文件系统类型被挂载时触发。
on fs-early <fs_type>:在早期文件系统挂载阶段触发。
on fs-late <fs_type>:在后期文件系统挂载阶段触发。
on post-fs:在post-fs阶段触发。
on post-fs-data:在post-fs-data阶段触发。
on boot-success:在系统启动成功后触发。
on property:init.svc.adbd=running:当adbd服务启动完成时触发。
这些Trigger条件可以根据需要在init.rc文件中进行配置和使用,用于触发特定的操作和事件。每个Trigger条件都有特定的语法和用法,需要根据具体情况进行正确配置。

参考

https://d0nuts33.github.io/2023/02/25/zygisk%E5%8E%9F%E7%90%86/index.html