Magisk/native/jni/init/twostage.cpp
2020-04-12 05:34:56 -07:00

175 lines
4.3 KiB
C++

#include <sys/ptrace.h>
#include <sys/wait.h>
#include <magisk.hpp>
#include <utils.hpp>
#include <logging.hpp>
#include <socket.hpp>
#include "init.hpp"
using namespace std;
static void patch_fstab(const string &fstab) {
string patched = fstab + ".p";
FILE *fp = xfopen(patched.data(), "we");
file_readline(fstab.data(), [=](string_view l) -> bool {
if (l[0] == '#' || l.length() == 1)
return true;
char *line = (char *) l.data();
int src0, src1, mnt0, mnt1, type0, type1, opt0, opt1, flag0, flag1;
sscanf(line, "%n%*s%n %n%*s%n %n%*s%n %n%*s%n %n%*s%n",
&src0, &src1, &mnt0, &mnt1, &type0, &type1, &opt0, &opt1, &flag0, &flag1);
const char *src, *mnt, *type, *opt, *flag;
src = &line[src0];
line[src1] = '\0';
mnt = &line[mnt0];
line[mnt1] = '\0';
type = &line[type0];
line[type1] = '\0';
opt = &line[opt0];
line[opt1] = '\0';
flag = &line[flag0];
line[flag1] = '\0';
// Redirect system to system_root
if (mnt == "/system"sv)
mnt = "/system_root";
fprintf(fp, "%s %s %s %s %s\n", src, mnt, type, opt, flag);
return true;
});
fclose(fp);
// Replace old fstab
clone_attr(fstab.data(), patched.data());
rename(patched.data(), fstab.data());
}
#define FSR "/first_stage_ramdisk"
void ForcedFirstStageInit::prepare() {
// It is actually possible to NOT have FSR, create it just in case
xmkdir(FSR, 0755);
if (auto dir = xopen_dir(FSR); dir) {
string fstab(FSR "/");
for (dirent *de; (de = xreaddir(dir.get()));) {
if (strstr(de->d_name, "fstab")) {
fstab += de->d_name;
break;
}
}
if (fstab.length() == sizeof(FSR))
return;
patch_fstab(fstab);
} else {
return;
}
// Move stuffs for next stage
xmkdir(FSR "/system", 0755);
xmkdir(FSR "/system/bin", 0755);
rename("/init", FSR "/system/bin/init");
symlink("/system/bin/init", FSR "/init");
xmkdir(FSR "/.backup", 0);
rename("/.backup/.magisk", FSR "/.backup/.magisk");
rename("/overlay.d", FSR "/overlay.d");
}
void FirstStageInit::prepare() {
auto dir = xopen_dir("/");
for (dirent *de; (de = xreaddir(dir.get()));) {
if (strstr(de->d_name, "fstab")) {
patch_fstab(de->d_name);
break;
}
}
// Move stuffs for next stage
xmkdir("/system", 0755);
xmkdir("/system/bin", 0755);
rename("/init", "/system/bin/init");
rename("/.backup/init", "/init");
}
static inline long xptrace(int request, pid_t pid, void *addr, void *data) {
long ret = ptrace(request, pid, addr, data);
if (ret < 0)
PLOGE("ptrace %d", pid);
return ret;
}
static inline long xptrace(int request, pid_t pid, void *addr = nullptr, intptr_t data = 0) {
return xptrace(request, pid, addr, reinterpret_cast<void *>(data));
}
void SARFirstStageInit::traced_exec_init() {
int pid = getpid();
// Block SIGUSR1
sigset_t block, old;
sigemptyset(&block);
sigaddset(&block, SIGUSR1);
sigprocmask(SIG_BLOCK, &block, &old);
if (int child = xfork(); child) {
LOGD("init tracer [%d]\n", child);
// Wait for children to attach
int sig;
sigwait(&block, &sig);
// Restore sigmask
sigprocmask(SIG_BLOCK, &old, nullptr);
// Re-exec init
exec_init();
} else {
// Attach to parent to trace exec
xptrace(PTRACE_ATTACH, pid);
waitpid(pid, nullptr, __WALL | __WNOTHREAD);
xptrace(PTRACE_SETOPTIONS, pid, nullptr, PTRACE_O_TRACEEXEC);
xptrace(PTRACE_CONT, pid, 0, SIGUSR1);
// Wait for execve
waitpid(pid, nullptr, __WALL | __WNOTHREAD);
// Swap out init with bind mount
xmount("tmpfs", "/dev", "tmpfs", 0, "mode=755");
int init = xopen("/dev/magiskinit", O_CREAT | O_WRONLY, 0750);
write(init, self.buf, self.sz);
close(init);
xmount("/dev/magiskinit", "/init", nullptr, MS_BIND, nullptr);
xumount2("/dev", MNT_DETACH);
// Establish socket for 2nd stage ack
struct sockaddr_un sun;
int sockfd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
xbind(sockfd, (struct sockaddr*) &sun, setup_sockaddr(&sun, INIT_SOCKET));
xlisten(sockfd, 1);
// Resume init
xptrace(PTRACE_DETACH, pid);
// Wait for second stage ack
int client = xaccept4(sockfd, nullptr, nullptr, SOCK_CLOEXEC);
// Write backup files
char *tmp_dir = read_string(client);
chdir(tmp_dir);
free(tmp_dir);
int cfg = xopen(INTLROOT "/config", O_WRONLY | O_CREAT, 0);
xwrite(cfg, config.buf, config.sz);
close(cfg);
restore_folder(ROOTOVL, overlays);
// Ack and bail out!
write(sockfd, &sockfd, sizeof(sockfd));
close(client);
close(sockfd);
exit(0);
}
}