mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-11-23 20:19:45 +00:00
Make denylist work when zygisk is disabled
Co-authored-by: topjohnwu <topjohnwu@gmail.com>
This commit is contained in:
parent
652a26d5d9
commit
60e8415369
@ -227,25 +227,14 @@ object Zygisk : BaseSettingsItem.Toggle() {
|
||||
get() = Config.zygisk
|
||||
set(value) {
|
||||
Config.zygisk = value
|
||||
DenyList.isEnabled = value
|
||||
DenyListConfig.isEnabled = value
|
||||
notifyPropertyChanged(BR.description)
|
||||
DenyList.notifyPropertyChanged(BR.description)
|
||||
}
|
||||
val mismatch get() = value != Info.isZygiskEnabled
|
||||
}
|
||||
|
||||
object DenyList : BaseSettingsItem.Toggle() {
|
||||
override val title = R.string.settings_denylist_title.asText()
|
||||
override val description get() =
|
||||
if (isEnabled) {
|
||||
if (Zygisk.mismatch)
|
||||
R.string.reboot_apply_change.asText()
|
||||
else
|
||||
R.string.settings_denylist_summary.asText()
|
||||
} else {
|
||||
R.string.settings_denylist_error.asText(R.string.zygisk.asText())
|
||||
}
|
||||
override val description get() = R.string.settings_denylist_summary.asText()
|
||||
|
||||
override var value = Config.denyList
|
||||
set(value) {
|
||||
@ -260,18 +249,11 @@ object DenyList : BaseSettingsItem.Toggle() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun refresh() {
|
||||
isEnabled = Zygisk.value
|
||||
}
|
||||
}
|
||||
|
||||
object DenyListConfig : BaseSettingsItem.Blank() {
|
||||
override val title = R.string.settings_denylist_config_title.asText()
|
||||
override val description = R.string.settings_denylist_config_summary.asText()
|
||||
override fun refresh() {
|
||||
isEnabled = Zygisk.value
|
||||
}
|
||||
}
|
||||
|
||||
// --- Superuser
|
||||
|
@ -37,7 +37,8 @@ LOCAL_SRC_FILES := \
|
||||
core/zygisk/module.cpp \
|
||||
core/zygisk/hook.cpp \
|
||||
core/deny/cli.cpp \
|
||||
core/deny/utils.cpp
|
||||
core/deny/utils.cpp \
|
||||
core/deny/logcat.cpp
|
||||
|
||||
LOCAL_LDLIBS := -llog
|
||||
LOCAL_LDFLAGS := -Wl,--dynamic-list=src/exported_sym.txt
|
||||
|
@ -53,8 +53,7 @@ void denylist_handler(int client, const sock_cred *cred) {
|
||||
ls_list(client);
|
||||
return;
|
||||
case DenyRequest::STATUS:
|
||||
res = (zygisk_enabled && denylist_enforced)
|
||||
? DenyResponse::ENFORCED : DenyResponse::NOT_ENFORCED;
|
||||
res = denylist_enforced ? DenyResponse::ENFORCED : DenyResponse::NOT_ENFORCED;
|
||||
break;
|
||||
default:
|
||||
// Unknown request code
|
||||
|
@ -44,3 +44,7 @@ int disable_deny();
|
||||
int add_list(int client);
|
||||
int rm_list(int client);
|
||||
void ls_list(int client);
|
||||
|
||||
bool proc_context_match(int pid, std::string_view context);
|
||||
void *logcat(void *arg);
|
||||
extern bool logcat_exit;
|
||||
|
266
native/src/core/deny/logcat.cpp
Normal file
266
native/src/core/deny/logcat.cpp
Normal file
@ -0,0 +1,266 @@
|
||||
#include <unistd.h>
|
||||
#include <string>
|
||||
#include <cinttypes>
|
||||
#include <android/log.h>
|
||||
|
||||
#include <base.hpp>
|
||||
|
||||
#include "deny.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
extern "C" {
|
||||
|
||||
struct logger_entry {
|
||||
uint16_t len; /* length of the payload */
|
||||
uint16_t hdr_size; /* sizeof(struct logger_entry) */
|
||||
int32_t pid; /* generating process's pid */
|
||||
uint32_t tid; /* generating process's tid */
|
||||
uint32_t sec; /* seconds since Epoch */
|
||||
uint32_t nsec; /* nanoseconds */
|
||||
uint32_t lid; /* log id of the payload, bottom 4 bits currently */
|
||||
uint32_t uid; /* generating process's uid */
|
||||
};
|
||||
|
||||
#define LOGGER_ENTRY_MAX_LEN (5 * 1024)
|
||||
struct log_msg {
|
||||
union [[gnu::aligned(4)]] {
|
||||
unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1];
|
||||
struct logger_entry entry;
|
||||
};
|
||||
};
|
||||
|
||||
typedef struct AndroidLogEntry_t {
|
||||
time_t tv_sec;
|
||||
long tv_nsec;
|
||||
android_LogPriority priority;
|
||||
int32_t uid;
|
||||
int32_t pid;
|
||||
int32_t tid;
|
||||
const char *tag;
|
||||
size_t tagLen;
|
||||
size_t messageLen;
|
||||
const char *message;
|
||||
} AndroidLogEntry;
|
||||
|
||||
[[gnu::weak]] struct logger_list *android_logger_list_alloc(int mode, unsigned int tail, pid_t pid);
|
||||
[[gnu::weak]] void android_logger_list_free(struct logger_list *list);
|
||||
[[gnu::weak]] int android_logger_list_read(struct logger_list *list, struct log_msg *log_msg);
|
||||
[[gnu::weak]] struct logger *android_logger_open(struct logger_list *list, log_id_t id);
|
||||
[[gnu::weak]] int android_log_processLogBuffer(struct logger_entry *buf, AndroidLogEntry *entry);
|
||||
|
||||
typedef struct [[gnu::packed]] {
|
||||
int32_t tag; // Little Endian Order
|
||||
} android_event_header_t;
|
||||
|
||||
typedef struct [[gnu::packed]] {
|
||||
int8_t type; // EVENT_TYPE_INT
|
||||
int32_t data; // Little Endian Order
|
||||
} android_event_int_t;
|
||||
|
||||
typedef struct [[gnu::packed]] {
|
||||
int8_t type; // EVENT_TYPE_STRING;
|
||||
int32_t length; // Little Endian Order
|
||||
char data[];
|
||||
} android_event_string_t;
|
||||
|
||||
typedef struct [[gnu::packed]] {
|
||||
int8_t type; // EVENT_TYPE_LIST
|
||||
int8_t element_count;
|
||||
} android_event_list_t;
|
||||
|
||||
// 30014 am_proc_start (User|1|5),(PID|1|5),(UID|1|5),(Process Name|3),(Type|3),(Component|3)
|
||||
typedef struct [[gnu::packed]] {
|
||||
android_event_header_t tag;
|
||||
android_event_list_t list;
|
||||
android_event_int_t user;
|
||||
android_event_int_t pid;
|
||||
android_event_int_t uid;
|
||||
android_event_string_t process_name;
|
||||
// android_event_string_t type;
|
||||
// android_event_string_t component;
|
||||
} android_event_am_proc_start;
|
||||
|
||||
// 3040 boot_progress_ams_ready (time|2|3)
|
||||
|
||||
}
|
||||
|
||||
// zygote pid -> mnt ns
|
||||
static map<int, struct stat> zygote_map;
|
||||
bool logcat_exit;
|
||||
|
||||
static int read_ns(const int pid, struct stat *st) {
|
||||
char path[32];
|
||||
sprintf(path, "/proc/%d/ns/mnt", pid);
|
||||
return stat(path, st);
|
||||
}
|
||||
|
||||
static int parse_ppid(int pid) {
|
||||
char path[32];
|
||||
int ppid;
|
||||
sprintf(path, "/proc/%d/stat", pid);
|
||||
auto stat = open_file(path, "re");
|
||||
if (!stat) return -1;
|
||||
// PID COMM STATE PPID .....
|
||||
fscanf(stat.get(), "%*d %*s %*c %d", &ppid);
|
||||
return ppid;
|
||||
}
|
||||
|
||||
static void check_zygote() {
|
||||
zygote_map.clear();
|
||||
auto proc = open("/proc", O_RDONLY | O_CLOEXEC);
|
||||
auto proc_dir = xopen_dir(proc);
|
||||
if (!proc_dir) return;
|
||||
dirent *entry;
|
||||
int pid;
|
||||
struct stat st{};
|
||||
while ((entry = readdir(proc_dir.get()))) {
|
||||
pid = parse_int(entry->d_name);
|
||||
if (pid <= 0) continue;
|
||||
if (fstatat(proc, entry->d_name, &st, 0)) continue;
|
||||
if (st.st_uid != 0) continue;
|
||||
if (proc_context_match(pid, "u:r:zygote:s0") && parse_ppid(pid) == 1) {
|
||||
if (read_ns(pid, &st) == 0) {
|
||||
LOGI("logcat: zygote PID=[%d]\n", pid);
|
||||
zygote_map[pid] = st;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_proc(int pid, bool app_zygote = false) {
|
||||
if (fork_dont_care() == 0) {
|
||||
if (app_zygote) {
|
||||
revert_unmount(pid);
|
||||
kill(pid, SIGCONT);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
int ppid = parse_ppid(pid);
|
||||
auto it = zygote_map.find(ppid);
|
||||
if (it == zygote_map.end()) {
|
||||
LOGW("logcat: skip PID=[%d] PPID=[%d]\n", pid, ppid);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
char path[16];
|
||||
struct stat st{};
|
||||
sprintf(path, "/proc/%d", pid);
|
||||
while (read_ns(pid, &st) == 0 && it->second.st_ino == st.st_ino) {
|
||||
if (stat(path, &st) == 0 && st.st_uid == 0) {
|
||||
usleep(10 * 1000);
|
||||
} else {
|
||||
LOGW("logcat: skip PID=[%d] UID=[%d]\n", pid, st.st_uid);
|
||||
_exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
revert_unmount(pid);
|
||||
_exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void process_main_buffer(struct logger_entry *buf) {
|
||||
AndroidLogEntry entry;
|
||||
if (android_log_processLogBuffer(buf, &entry) < 0) return;
|
||||
entry.tagLen--;
|
||||
auto tag = string_view(entry.tag, entry.tagLen);
|
||||
|
||||
static bool ready = false;
|
||||
if (tag == "AppZygote") {
|
||||
if (entry.uid != 1000) return;
|
||||
if (entry.message[0] == 'S') {
|
||||
ready = true;
|
||||
} else {
|
||||
ready = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ready || tag != "AppZygoteInit") return;
|
||||
if (!proc_context_match(buf->pid, "u:r:app_zygote:s0")) return;
|
||||
ready = false;
|
||||
|
||||
char cmdline[1024];
|
||||
sprintf(cmdline, "/proc/%d/cmdline", buf->pid);
|
||||
if (auto f = open_file(cmdline, "re")) {
|
||||
fgets(cmdline, sizeof(cmdline), f.get());
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_deny_target(entry.uid, cmdline)) {
|
||||
kill(buf->pid, SIGSTOP);
|
||||
LOGI("logcat: [%s] PID=[%d] UID=[%d]\n", cmdline, buf->pid, entry.uid);
|
||||
handle_proc(buf->pid, true);
|
||||
}
|
||||
}
|
||||
|
||||
static void process_events_buffer(struct logger_entry *buf) {
|
||||
if (buf->uid != 1000) return;
|
||||
auto *event_data = reinterpret_cast<const unsigned char *>(buf) + buf->hdr_size;
|
||||
auto *event_header = reinterpret_cast<const android_event_header_t *>(event_data);
|
||||
if (event_header->tag == 30014) {
|
||||
auto *am_proc_start = reinterpret_cast<const android_event_am_proc_start *>(event_data);
|
||||
auto proc = string_view(am_proc_start->process_name.data,
|
||||
am_proc_start->process_name.length);
|
||||
if (is_deny_target(am_proc_start->uid.data, proc)) {
|
||||
LOGI("logcat: [%.*s] PID=[%d] UID=[%d]\n",
|
||||
am_proc_start->process_name.length, am_proc_start->process_name.data,
|
||||
am_proc_start->pid.data, am_proc_start->uid.data);
|
||||
handle_proc(am_proc_start->pid.data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (event_header->tag == 3040) {
|
||||
LOGD("logcat: soft reboot\n");
|
||||
check_zygote();
|
||||
}
|
||||
}
|
||||
|
||||
[[noreturn]] void run() {
|
||||
while (true) {
|
||||
const unique_ptr<logger_list, decltype(&android_logger_list_free)> logger_list{
|
||||
android_logger_list_alloc(0, 1, 0), &android_logger_list_free};
|
||||
|
||||
for (log_id id: {LOG_ID_MAIN, LOG_ID_EVENTS}) {
|
||||
auto *logger = android_logger_open(logger_list.get(), id);
|
||||
if (logger == nullptr) continue;
|
||||
}
|
||||
|
||||
struct log_msg msg{};
|
||||
while (true) {
|
||||
if (logcat_exit) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (android_logger_list_read(logger_list.get(), &msg) <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (msg.entry.lid) {
|
||||
case LOG_ID_EVENTS:
|
||||
process_events_buffer(&msg.entry);
|
||||
break;
|
||||
case LOG_ID_MAIN:
|
||||
process_main_buffer(&msg.entry);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (logcat_exit) {
|
||||
break;
|
||||
}
|
||||
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
LOGD("logcat: terminate\n");
|
||||
pthread_exit(nullptr);
|
||||
}
|
||||
|
||||
void *logcat(void *) {
|
||||
check_zygote();
|
||||
run();
|
||||
}
|
@ -33,8 +33,6 @@ static pthread_mutex_t data_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
atomic<bool> denylist_enforced = false;
|
||||
|
||||
#define do_kill (zygisk_enabled && denylist_enforced)
|
||||
|
||||
static void rescan_apps() {
|
||||
LOGD("denylist: rescanning apps\n");
|
||||
|
||||
@ -121,7 +119,7 @@ static bool proc_name_match(int pid, string_view name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool proc_context_match(int pid, string_view context) {
|
||||
bool proc_context_match(int pid, string_view context) {
|
||||
char buf[PATH_MAX];
|
||||
sprintf(buf, "/proc/%d/attr/current", pid);
|
||||
if (auto fp = open_file(buf, "re")) {
|
||||
@ -186,7 +184,7 @@ static bool add_hide_set(const char *pkg, const char *proc) {
|
||||
if (!p.second)
|
||||
return false;
|
||||
LOGI("denylist add: [%s/%s]\n", pkg, proc);
|
||||
if (!do_kill)
|
||||
if (!denylist_enforced)
|
||||
return true;
|
||||
if (str_eql(pkg, ISOLATED_MAGIC)) {
|
||||
// Kill all matching isolated processes
|
||||
@ -350,15 +348,21 @@ int enable_deny() {
|
||||
|
||||
LOGI("* Enable DenyList\n");
|
||||
|
||||
denylist_enforced = true;
|
||||
|
||||
if (!ensure_data()) {
|
||||
denylist_enforced = false;
|
||||
return DenyResponse::ERROR;
|
||||
}
|
||||
|
||||
if (!zygisk_enabled) {
|
||||
logcat_exit = false;
|
||||
if (new_daemon_thread(&logcat))
|
||||
return DenyResponse::ERROR;
|
||||
}
|
||||
|
||||
denylist_enforced = true;
|
||||
|
||||
// On Android Q+, also kill blastula pool and all app zygotes
|
||||
if (SDK_INT >= 29 && zygisk_enabled) {
|
||||
if (SDK_INT >= 29) {
|
||||
kill_process("usap32", true);
|
||||
kill_process("usap64", true);
|
||||
kill_process<&proc_context_match>("u:r:app_zygote:s0", true);
|
||||
@ -373,6 +377,10 @@ int disable_deny() {
|
||||
if (denylist_enforced) {
|
||||
denylist_enforced = false;
|
||||
LOGI("* Disable DenyList\n");
|
||||
|
||||
if (!zygisk_enabled) {
|
||||
logcat_exit = true;
|
||||
}
|
||||
}
|
||||
update_deny_config();
|
||||
return DenyResponse::OK;
|
||||
|
@ -91,3 +91,4 @@ extern std::atomic<bool> denylist_enforced;
|
||||
int denylist_cli(int argc, char **argv);
|
||||
void initialize_denylist();
|
||||
bool is_deny_target(int uid, std::string_view process);
|
||||
void revert_unmount(int pid = -1) noexcept;
|
||||
|
@ -72,6 +72,8 @@ pub mod ffi {
|
||||
#[cxx_name = "resolve_preinit_dir_rs"]
|
||||
fn resolve_preinit_dir(base_dir: Utf8CStrRef) -> String;
|
||||
|
||||
fn switch_mnt_ns(pid: i32) -> i32;
|
||||
|
||||
#[cxx_name = "MagiskD"]
|
||||
type CxxMagiskD;
|
||||
fn post_fs_data(self: &CxxMagiskD) -> bool;
|
||||
@ -90,7 +92,7 @@ pub mod ffi {
|
||||
fn read_certificate(fd: i32, version: i32) -> Vec<u8>;
|
||||
fn setup_mounts();
|
||||
fn find_preinit_device() -> String;
|
||||
fn revert_unmount();
|
||||
fn revert_unmount(pid: i32);
|
||||
unsafe fn persist_get_prop(name: Utf8CStrRef, prop_cb: Pin<&mut PropCb>);
|
||||
unsafe fn persist_get_props(prop_cb: Pin<&mut PropCb>);
|
||||
unsafe fn persist_delete_prop(name: Utf8CStrRef) -> bool;
|
||||
|
@ -10,7 +10,7 @@ use base::{
|
||||
};
|
||||
|
||||
use crate::consts::{MODULEMNT, MODULEROOT, PREINITDEV, PREINITMIRR, WORKERDIR};
|
||||
use crate::ffi::{get_magisk_tmp, resolve_preinit_dir};
|
||||
use crate::ffi::{get_magisk_tmp, resolve_preinit_dir, switch_mnt_ns};
|
||||
use crate::get_prop;
|
||||
|
||||
pub fn setup_mounts() {
|
||||
@ -275,7 +275,14 @@ pub fn find_preinit_device() -> String {
|
||||
.to_string()
|
||||
}
|
||||
|
||||
pub fn revert_unmount() {
|
||||
pub fn revert_unmount(pid: i32) {
|
||||
if pid > 0 {
|
||||
if switch_mnt_ns(pid) != 0 {
|
||||
return;
|
||||
}
|
||||
debug!("denylist: handling PID=[{}]", pid);
|
||||
}
|
||||
|
||||
let mut targets = Vec::new();
|
||||
|
||||
// Unmount Magisk tmpfs and mounts from module files
|
||||
|
Loading…
Reference in New Issue
Block a user