You've already forked Zygisk-Assistant
mirror of
https://github.com/snake-4/Zygisk-Assistant.git
synced 2025-09-06 06:37:02 +00:00
Added usermode unmounter and mounts parser
+ Added a mounts parser implementation using mntent API. + Added logging functions. + Added usermode unmounter which calls unshare pre app-specialization. * Updated module.prop.
This commit is contained in:
@@ -2,7 +2,7 @@ LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := zygisk
|
||||
LOCAL_SRC_FILES := main.cpp
|
||||
LOCAL_SRC_FILES := mount_parser.cpp unmount.cpp main.cpp
|
||||
LOCAL_STATIC_LIBRARIES := libcxx
|
||||
LOCAL_LDLIBS := -llog
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
15
module/jni/logging.hpp
Normal file
15
module/jni/logging.hpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
#include <android/log.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifndef NDEBUG
|
||||
static constexpr auto TAG = "ZygiskAssistant/JNI";
|
||||
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
|
||||
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
|
||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
|
||||
#else
|
||||
#define LOGD(...)
|
||||
#define LOGI(...)
|
||||
#define LOGE(...)
|
||||
#endif
|
||||
@@ -1,14 +1,15 @@
|
||||
#include <cstdlib>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <android/log.h>
|
||||
#include <sched.h>
|
||||
|
||||
#include "zygisk.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
using zygisk::Api;
|
||||
using zygisk::AppSpecializeArgs;
|
||||
using zygisk::ServerSpecializeArgs;
|
||||
|
||||
void do_unmount();
|
||||
|
||||
class ZygiskModule : public zygisk::ModuleBase
|
||||
{
|
||||
public:
|
||||
@@ -25,22 +26,30 @@ public:
|
||||
uint32_t flags = api->getFlags();
|
||||
bool isRoot = (flags & zygisk::StateFlag::PROCESS_GRANTED_ROOT) != 0;
|
||||
bool isOnDenylist = (flags & zygisk::StateFlag::PROCESS_ON_DENYLIST) != 0;
|
||||
|
||||
if (!isRoot && isOnDenylist && args->uid > 1000)
|
||||
{
|
||||
api->setOption(zygisk::Option::FORCE_DENYLIST_UNMOUNT);
|
||||
}
|
||||
}
|
||||
LOGD("Unmounting in preAppSpecialize for pid=%d uid=%d", getpid(), args->uid);
|
||||
|
||||
void preServerSpecialize(ServerSpecializeArgs *args)
|
||||
{
|
||||
api->setOption(zygisk::Option::DLCLOSE_MODULE_LIBRARY);
|
||||
/*
|
||||
* preAppSpecialize is before ensureInAppMountNamespace.
|
||||
* postAppSpecialize is after seccomp setup.
|
||||
* So we unshare here to create a pseudo app mount namespace
|
||||
*/
|
||||
if (unshare(CLONE_NEWNS) == 0)
|
||||
{
|
||||
LOGD("unshare(CLONE_NEWNS) returned 0");
|
||||
do_unmount();
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGE("unshare(CLONE_NEWNS) returned -1: %d (%s)", errno, strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Api *api;
|
||||
JNIEnv *env;
|
||||
|
||||
};
|
||||
|
||||
REGISTER_ZYGISK_MODULE(ZygiskModule)
|
||||
|
||||
83
module/jni/mount_parser.cpp
Normal file
83
module/jni/mount_parser.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <mntent.h>
|
||||
|
||||
#include "mount_parser.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
mount_entry_t::mount_entry_t(::mntent *entry)
|
||||
: fsname(entry->mnt_fsname), dir(entry->mnt_dir), type(entry->mnt_type), freq(entry->mnt_freq), passno(entry->mnt_passno)
|
||||
{
|
||||
parseMountOptions(entry->mnt_opts);
|
||||
}
|
||||
|
||||
const std::string &mount_entry_t::getFsName() const
|
||||
{
|
||||
return fsname;
|
||||
}
|
||||
|
||||
const std::string &mount_entry_t::getMountPoint() const
|
||||
{
|
||||
return dir;
|
||||
}
|
||||
|
||||
const std::string &mount_entry_t::getType() const
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
const std::unordered_map<std::string, std::string> &mount_entry_t::getOptions() const
|
||||
{
|
||||
return opts_map;
|
||||
}
|
||||
|
||||
int mount_entry_t::getDumpFrequency() const
|
||||
{
|
||||
return freq;
|
||||
}
|
||||
|
||||
int mount_entry_t::getPassNumber() const
|
||||
{
|
||||
return passno;
|
||||
}
|
||||
|
||||
void mount_entry_t::parseMountOptions(const std::string &input)
|
||||
{
|
||||
std::istringstream iss(input);
|
||||
std::string token;
|
||||
while (std::getline(iss, token, ','))
|
||||
{
|
||||
std::istringstream tokenStream(token);
|
||||
std::string key, value;
|
||||
|
||||
if (std::getline(tokenStream, key, '='))
|
||||
{
|
||||
std::getline(tokenStream, value); // Put what's left in the stream to value, could be empty
|
||||
opts_map[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<mount_entry_t> parseMountsFromPath(const char *path)
|
||||
{
|
||||
std::vector<mount_entry_t> result;
|
||||
|
||||
FILE *file = setmntent(path, "r");
|
||||
if (file == NULL)
|
||||
{
|
||||
LOGE("setmntent(\"%s\", \"r\") returned NULL: %d (%s)", path, errno, strerror(errno));
|
||||
return result;
|
||||
}
|
||||
|
||||
struct mntent *entry;
|
||||
while ((entry = getmntent(file)) != NULL)
|
||||
{
|
||||
result.emplace_back(mount_entry_t(entry));
|
||||
}
|
||||
|
||||
endmntent(file);
|
||||
return result;
|
||||
}
|
||||
29
module/jni/mount_parser.hpp
Normal file
29
module/jni/mount_parser.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <mntent.h>
|
||||
|
||||
class mount_entry_t
|
||||
{
|
||||
public:
|
||||
mount_entry_t(::mntent *entry);
|
||||
const std::string &getFsName() const;
|
||||
const std::string &getMountPoint() const;
|
||||
const std::string &getType() const;
|
||||
const std::unordered_map<std::string, std::string> &getOptions() const;
|
||||
int getDumpFrequency() const;
|
||||
int getPassNumber() const;
|
||||
|
||||
private:
|
||||
void parseMountOptions(const std::string &input);
|
||||
|
||||
std::string fsname;
|
||||
std::string dir;
|
||||
std::string type;
|
||||
std::unordered_map<std::string, std::string> opts_map;
|
||||
int freq;
|
||||
int passno;
|
||||
};
|
||||
|
||||
std::vector<mount_entry_t> parseMountsFromPath(const char *path);
|
||||
73
module/jni/unmount.cpp
Normal file
73
module/jni/unmount.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include "zygisk.hpp"
|
||||
#include "logging.hpp"
|
||||
#include "mount_parser.hpp"
|
||||
|
||||
constexpr std::array<const char *, 4> fsname_list = {"KSU", "APatch", "magisk", "worker"};
|
||||
|
||||
static bool shouldUnmount(const mount_entry_t &info)
|
||||
{
|
||||
const auto &mountPoint = info.getMountPoint();
|
||||
const auto &type = info.getType();
|
||||
const auto &options = info.getOptions();
|
||||
|
||||
// Unmount everything mounted to /data/adb
|
||||
if (mountPoint.rfind("/data/adb", 0) == 0)
|
||||
return true;
|
||||
|
||||
// Unmount all module overlayfs and tmpfs
|
||||
bool doesFsnameMatch = std::find(fsname_list.begin(), fsname_list.end(), info.getFsName()) != fsname_list.end();
|
||||
if ((type == "overlay" || type == "tmpfs") && doesFsnameMatch)
|
||||
return true;
|
||||
|
||||
// Unmount all overlayfs with lowerdir/upperdir/workdir starting with /data/adb
|
||||
if (type == "overlay")
|
||||
{
|
||||
const auto &lowerdir = options.find("lowerdir");
|
||||
const auto &upperdir = options.find("upperdir");
|
||||
const auto &workdir = options.find("workdir");
|
||||
|
||||
if (lowerdir != options.end() && lowerdir->second.rfind("/data/adb", 0) == 0)
|
||||
return true;
|
||||
|
||||
if (upperdir != options.end() && upperdir->second.rfind("/data/adb", 0) == 0)
|
||||
return true;
|
||||
|
||||
if (workdir != options.end() && workdir->second.rfind("/data/adb", 0) == 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void do_unmount()
|
||||
{
|
||||
std::vector<std::string> mountPoints;
|
||||
for (auto &info : parseMountsFromPath("/proc/self/mounts"))
|
||||
{
|
||||
if (shouldUnmount(info))
|
||||
{
|
||||
mountPoints.push_back(info.getMountPoint());
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by string lengths, descending
|
||||
std::sort(mountPoints.begin(), mountPoints.end(), [](const auto &lhs, const auto &rhs)
|
||||
{ return lhs.size() > rhs.size(); });
|
||||
|
||||
for (const auto &mountPoint : mountPoints)
|
||||
{
|
||||
if (umount2(mountPoint.c_str(), MNT_DETACH) == 0)
|
||||
{
|
||||
LOGD("umount2(\"%s\", MNT_DETACH) returned 0", mountPoint.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGE("umount2(\"%s\", MNT_DETACH) returned -1: %d (%s)", mountPoint.c_str(), errno, strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,5 +2,5 @@ id=${moduleId}
|
||||
name=${moduleName}
|
||||
version=${versionName}
|
||||
versionCode=${versionCode}
|
||||
author=snake-4 & yervant7
|
||||
description=DLCLOSE_MODULE_LIBRARY and FORCE_DENYLIST_UNMOUNT for non-root processes.
|
||||
author=snake-4
|
||||
description=Zygisk module to hide mounts.
|
||||
|
||||
Reference in New Issue
Block a user