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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
static void forkAndSpecializePre(
JNIEnv* env, jclass, jint* _uid, jint* gid, jintArray* gids, jint* runtimeFlags,
jobjectArray* rlimits, jint* mountExternal, jstring* seInfo, jstring* niceName,
jintArray* fdsToClose, jintArray* fdsToIgnore, jboolean* is_child_zygote,
jstring* instructionSet, jstring* appDataDir, jboolean* isTopApp,
jobjectArray* pkgDataInfoList,
jobjectArray* whitelistedDataInfoList, jboolean* bindMountAppDataDirs,
jboolean* bindMountAppStorageDirs) {
InitProcessState(*_uid, *is_child_zygote);
nice_name_ = niceName;
if (hide_isolated_) {
no_new_ns_ = EnsureSeparatedNamespace(mountExternal, *bindMountAppDataDirs, *bindMountAppStorageDirs);
MaybeInitNsHolder(env);
}
}
// Maybe change the mount external mode to make sure the new process will call unshare().
// Returns true if we don't need a new ns for this process
bool EnsureSeparatedNamespace(jint* mountMode, jboolean bindMountAppDataDirs, jboolean bindMountAppStorageDirs) {
if (*mountMode == 0) {
// 这里和riru-unsharem模块一样,更改mountMode,强行产生新的namespace
bool no_need_newns = bindMountAppDataDirs == JNI_FALSE && bindMountAppStorageDirs == JNI_FALSE;
LOGI("Changed mount mode from NONE to DEFAULT and %s", no_need_newns ? "skip unshare" : "keep unshare");
*mountMode = 1;
return no_need_newns;
}
return false;
}
void MaybeInitNsHolder(JNIEnv* env) {
if (!use_nsholder_) return;
if (nsholder_mnt_ns_) {
if (access(nsholder_mnt_ns_, F_OK) != 0) {
// Maybe the nsholder died
LOGW("access %s failed with error %s", nsholder_mnt_ns_, strerror(errno));
if (nsholder_pid_ > 0) {
kill(nsholder_pid_, SIGKILL);
}
free(nsholder_mnt_ns_);
} else { // Still alive
return;
}
}
LOGI("Starting nsholder");
int read_fd, write_fd;
{
int pipe_fd[2];
if (pipe(pipe_fd) == -1) {
LOGE("Failed to create pipe for nsholder: %s", strerror(errno));
return;
} else {
read_fd = pipe_fd[0];
write_fd = pipe_fd[1];
}
}
nsholder_pid_ = orig_fork();
if (nsholder_pid_ < 0) { // failed, cleanup
LOGE("fork nsholder failed: %s", strerror(errno));
close(read_fd);
close(write_fd);
nsholder_mnt_ns_ = nullptr;
} else if (nsholder_pid_ == 0) { // child
close(read_fd);
if (orig_unshare(CLONE_NEWNS) == -1) {
LOGE("nsholder: failed to clone new ns: %s", strerror(errno));
WriteIntAndClose(write_fd, 1);
exit(1);
}
LOGI("Hiding Magisk in nsholder %d...", getpid());
HideMagisk();
LOGI("Unmounted magisk file system.");
// Change process name
{
jstring name = env->NewStringUTF(sizeof(void*) == 8 ? "nsholder64" : "nsholder32");
SetProcessName(env, name);
env->DeleteLocalRef(name);
}
// We're in the "cleaned" ns, notify the zygote we're ready and stop us
WriteIntAndClose(write_fd, 0);
// All done, but we should keep alive, because we need to keep the namespace
// If a fd references the namespace, the ns won't be destroyed
// but we need to open a fd in zygote, and Google don't want we opened new fd across fork,
// zygote will abort with error like "Not whitelisted (41): mnt:[4026533391]"
// We can manually call the Zygote.nativeAllowAcrossFork(), but this can be detected by app;
// or, we can use the "fdsToIgnore" argument, but for usap, forkApp() haven't the argument.
// To keep it simple, just let fd not opened in zygote
for (;;) {
// Note: SIGSTOP can't stop us here when magisk hide is enabled
// I think the magisk hiding catches our SIGSTOP and not handle it properly (didn't resend the signal)
// raise(SIGSTOP);
pause();
LOGW("nsholder wakes up unexpectedly, sleep again");
}
} else { // parent, wait the nsholder enter a "clean" ns
close(write_fd);
int status = ReadIntAndClose(read_fd);
if (status == 0) {
kill(nsholder_pid_, SIGSTOP); // make nsholder is stopped again
char mnt[32];
snprintf(mnt, sizeof(mnt), "/proc/%d/ns/mnt", nsholder_pid_);
LOGI("The nsholder is cleaned and stopped, mnt_ns is %s", mnt);
nsholder_mnt_ns_ = strdup(mnt);
return;
} else {
LOGE("Unexpected status %d received from the nsholder", status);
kill(nsholder_pid_, SIGKILL);
nsholder_pid_ = -1;
nsholder_mnt_ns_ = nullptr;
use_nsholder_ = false;
}
}
}
|