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
Moved headers to includes, various changes...
+ Added better UID checks. + Added MS_SLAVE root mount in the new mount namespace.
This commit is contained in:
219
module/jni/include/android_filesystem_config.h
Normal file
219
module/jni/include/android_filesystem_config.h
Normal file
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is consumed by build/tools/fs_config and is used
|
||||
* for generating various files. Anything #define AID_<name>
|
||||
* becomes the mapping for getpwnam/getpwuid, etc. The <name>
|
||||
* field is lowercased.
|
||||
* For example:
|
||||
* #define AID_FOO_BAR 6666 becomes a friendly name of "foo_bar"
|
||||
*
|
||||
* The above holds true with the exception of:
|
||||
* mediacodec
|
||||
* mediaex
|
||||
* mediadrm
|
||||
* Whose friendly names do not match the #define statements.
|
||||
*
|
||||
* This file must only be used for platform (Google managed, and submitted through AOSP), AIDs. 3rd
|
||||
* party AIDs must be added via config.fs, which will place them in the corresponding partition's
|
||||
* passwd and group files. There are ranges in this file reserved for AIDs for each 3rd party
|
||||
* partition, from which the system reads passwd and group files.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* This is the main Users and Groups config for the platform.
|
||||
* DO NOT EVER RENUMBER
|
||||
*/
|
||||
|
||||
#define AID_ROOT 0 /* traditional unix root user */
|
||||
/* The following are for LTP and should only be used for testing */
|
||||
#define AID_DAEMON 1 /* traditional unix daemon owner */
|
||||
#define AID_BIN 2 /* traditional unix binaries owner */
|
||||
|
||||
#define AID_SYSTEM 1000 /* system server */
|
||||
|
||||
#define AID_RADIO 1001 /* telephony subsystem, RIL */
|
||||
#define AID_BLUETOOTH 1002 /* bluetooth subsystem */
|
||||
#define AID_GRAPHICS 1003 /* graphics devices */
|
||||
#define AID_INPUT 1004 /* input devices */
|
||||
#define AID_AUDIO 1005 /* audio devices */
|
||||
#define AID_CAMERA 1006 /* camera devices */
|
||||
#define AID_LOG 1007 /* log devices */
|
||||
#define AID_COMPASS 1008 /* compass device */
|
||||
#define AID_MOUNT 1009 /* mountd socket */
|
||||
#define AID_WIFI 1010 /* wifi subsystem */
|
||||
#define AID_ADB 1011 /* android debug bridge (adbd) */
|
||||
#define AID_INSTALL 1012 /* group for installing packages */
|
||||
#define AID_MEDIA 1013 /* mediaserver process */
|
||||
#define AID_DHCP 1014 /* dhcp client */
|
||||
#define AID_SDCARD_RW 1015 /* external storage write access */
|
||||
#define AID_VPN 1016 /* vpn system */
|
||||
#define AID_KEYSTORE 1017 /* keystore subsystem */
|
||||
#define AID_USB 1018 /* USB devices */
|
||||
#define AID_DRM 1019 /* DRM server */
|
||||
#define AID_MDNSR 1020 /* MulticastDNSResponder (service discovery) */
|
||||
#define AID_GPS 1021 /* GPS daemon */
|
||||
#define AID_UNUSED1 1022 /* deprecated, DO NOT USE */
|
||||
#define AID_MEDIA_RW 1023 /* internal media storage write access */
|
||||
#define AID_MTP 1024 /* MTP USB driver access */
|
||||
#define AID_UNUSED2 1025 /* deprecated, DO NOT USE */
|
||||
#define AID_DRMRPC 1026 /* group for drm rpc */
|
||||
#define AID_NFC 1027 /* nfc subsystem */
|
||||
#define AID_SDCARD_R 1028 /* external storage read access */
|
||||
#define AID_CLAT 1029 /* clat part of nat464 */
|
||||
#define AID_LOOP_RADIO 1030 /* loop radio devices */
|
||||
#define AID_MEDIA_DRM 1031 /* MediaDrm plugins */
|
||||
#define AID_PACKAGE_INFO 1032 /* access to installed package details */
|
||||
#define AID_SDCARD_PICS 1033 /* external storage photos access */
|
||||
#define AID_SDCARD_AV 1034 /* external storage audio/video access */
|
||||
#define AID_SDCARD_ALL 1035 /* access all users external storage */
|
||||
#define AID_LOGD 1036 /* log daemon */
|
||||
#define AID_SHARED_RELRO 1037 /* creator of shared GNU RELRO files */
|
||||
#define AID_DBUS 1038 /* dbus-daemon IPC broker process */
|
||||
#define AID_TLSDATE 1039 /* tlsdate unprivileged user */
|
||||
#define AID_MEDIA_EX 1040 /* mediaextractor process */
|
||||
#define AID_AUDIOSERVER 1041 /* audioserver process */
|
||||
#define AID_METRICS_COLL 1042 /* metrics_collector process */
|
||||
#define AID_METRICSD 1043 /* metricsd process */
|
||||
#define AID_WEBSERV 1044 /* webservd process */
|
||||
#define AID_DEBUGGERD 1045 /* debuggerd unprivileged user */
|
||||
#define AID_MEDIA_CODEC 1046 /* mediacodec process */
|
||||
#define AID_CAMERASERVER 1047 /* cameraserver process */
|
||||
#define AID_FIREWALL 1048 /* firewalld process */
|
||||
#define AID_TRUNKS 1049 /* trunksd process (TPM daemon) */
|
||||
#define AID_NVRAM 1050 /* Access-controlled NVRAM */
|
||||
#define AID_DNS 1051 /* DNS resolution daemon (system: netd) */
|
||||
#define AID_DNS_TETHER 1052 /* DNS resolution daemon (tether: dnsmasq) */
|
||||
#define AID_WEBVIEW_ZYGOTE 1053 /* WebView zygote process */
|
||||
#define AID_VEHICLE_NETWORK 1054 /* Vehicle network service */
|
||||
#define AID_MEDIA_AUDIO 1055 /* GID for audio files on internal media storage */
|
||||
#define AID_MEDIA_VIDEO 1056 /* GID for video files on internal media storage */
|
||||
#define AID_MEDIA_IMAGE 1057 /* GID for image files on internal media storage */
|
||||
#define AID_TOMBSTONED 1058 /* tombstoned user */
|
||||
#define AID_MEDIA_OBB 1059 /* GID for OBB files on internal media storage */
|
||||
#define AID_ESE 1060 /* embedded secure element (eSE) subsystem */
|
||||
#define AID_OTA_UPDATE 1061 /* resource tracking UID for OTA updates */
|
||||
#define AID_AUTOMOTIVE_EVS 1062 /* Automotive rear and surround view system */
|
||||
#define AID_LOWPAN 1063 /* LoWPAN subsystem */
|
||||
#define AID_HSM 1064 /* hardware security module subsystem */
|
||||
#define AID_RESERVED_DISK 1065 /* GID that has access to reserved disk space */
|
||||
#define AID_STATSD 1066 /* statsd daemon */
|
||||
#define AID_INCIDENTD 1067 /* incidentd daemon */
|
||||
#define AID_SECURE_ELEMENT 1068 /* secure element subsystem */
|
||||
#define AID_LMKD 1069 /* low memory killer daemon */
|
||||
#define AID_LLKD 1070 /* live lock daemon */
|
||||
#define AID_IORAPD 1071 /* input/output readahead and pin daemon */
|
||||
#define AID_GPU_SERVICE 1072 /* GPU service daemon */
|
||||
#define AID_NETWORK_STACK 1073 /* network stack service */
|
||||
#define AID_GSID 1074 /* GSI service daemon */
|
||||
#define AID_FSVERITY_CERT 1075 /* fs-verity key ownership in keystore */
|
||||
#define AID_CREDSTORE 1076 /* identity credential manager service */
|
||||
#define AID_EXTERNAL_STORAGE 1077 /* Full external storage access including USB OTG volumes */
|
||||
#define AID_EXT_DATA_RW 1078 /* GID for app-private data directories on external storage */
|
||||
#define AID_EXT_OBB_RW 1079 /* GID for OBB directories on external storage */
|
||||
#define AID_CONTEXT_HUB 1080 /* GID for access to the Context Hub */
|
||||
/* Changes to this file must be made in AOSP, *not* in internal branches. */
|
||||
|
||||
#define AID_SHELL 2000 /* adb and debug shell user */
|
||||
#define AID_CACHE 2001 /* cache access */
|
||||
#define AID_DIAG 2002 /* access to diagnostic resources */
|
||||
|
||||
/* The range 2900-2999 is reserved for the vendor partition */
|
||||
/* Note that the two 'OEM' ranges pre-dated the vendor partition, so they take the legacy 'OEM'
|
||||
* name. Additionally, they pre-dated passwd/group files, so there are users and groups named oem_#
|
||||
* created automatically for all values in these ranges. If there is a user/group in a passwd/group
|
||||
* file corresponding to this range, both the oem_# and user/group names will resolve to the same
|
||||
* value. */
|
||||
#define AID_OEM_RESERVED_START 2900
|
||||
#define AID_OEM_RESERVED_END 2999
|
||||
|
||||
/* The 3000 series are intended for use as supplemental group id's only.
|
||||
* They indicate special Android capabilities that the kernel is aware of. */
|
||||
#define AID_NET_BT_ADMIN 3001 /* bluetooth: create any socket */
|
||||
#define AID_NET_BT 3002 /* bluetooth: create sco, rfcomm or l2cap sockets */
|
||||
#define AID_INET 3003 /* can create AF_INET and AF_INET6 sockets */
|
||||
#define AID_NET_RAW 3004 /* can create raw INET sockets */
|
||||
#define AID_NET_ADMIN 3005 /* can configure interfaces and routing tables. */
|
||||
#define AID_NET_BW_STATS 3006 /* read bandwidth statistics */
|
||||
#define AID_NET_BW_ACCT 3007 /* change bandwidth statistics accounting */
|
||||
#define AID_READPROC 3009 /* Allow /proc read access */
|
||||
#define AID_WAKELOCK 3010 /* Allow system wakelock read/write access */
|
||||
#define AID_UHID 3011 /* Allow read/write to /dev/uhid node */
|
||||
|
||||
/* The range 5000-5999 is also reserved for vendor partition. */
|
||||
#define AID_OEM_RESERVED_2_START 5000
|
||||
#define AID_OEM_RESERVED_2_END 5999
|
||||
|
||||
/* The range 6000-6499 is reserved for the system partition. */
|
||||
#define AID_SYSTEM_RESERVED_START 6000
|
||||
#define AID_SYSTEM_RESERVED_END 6499
|
||||
|
||||
/* The range 6500-6999 is reserved for the odm partition. */
|
||||
#define AID_ODM_RESERVED_START 6500
|
||||
#define AID_ODM_RESERVED_END 6999
|
||||
|
||||
/* The range 7000-7499 is reserved for the product partition. */
|
||||
#define AID_PRODUCT_RESERVED_START 7000
|
||||
#define AID_PRODUCT_RESERVED_END 7499
|
||||
|
||||
/* The range 7500-7999 is reserved for the system_ext partition. */
|
||||
#define AID_SYSTEM_EXT_RESERVED_START 7500
|
||||
#define AID_SYSTEM_EXT_RESERVED_END 7999
|
||||
|
||||
#define AID_EVERYBODY 9997 /* shared between all apps in the same profile */
|
||||
#define AID_MISC 9998 /* access to misc storage */
|
||||
#define AID_NOBODY 9999
|
||||
|
||||
#define AID_APP 10000 /* TODO: switch users over to AID_APP_START */
|
||||
#define AID_APP_START 10000 /* first app user */
|
||||
#define AID_APP_END 19999 /* last app user */
|
||||
|
||||
#define AID_CACHE_GID_START 20000 /* start of gids for apps to mark cached data */
|
||||
#define AID_CACHE_GID_END 29999 /* end of gids for apps to mark cached data */
|
||||
|
||||
#define AID_EXT_GID_START 30000 /* start of gids for apps to mark external data */
|
||||
#define AID_EXT_GID_END 39999 /* end of gids for apps to mark external data */
|
||||
|
||||
#define AID_EXT_CACHE_GID_START 40000 /* start of gids for apps to mark external cached data */
|
||||
#define AID_EXT_CACHE_GID_END 49999 /* end of gids for apps to mark external cached data */
|
||||
|
||||
#define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */
|
||||
#define AID_SHARED_GID_END 59999 /* end of gids for apps in each user to share */
|
||||
|
||||
/*
|
||||
* This is a magic number in the kernel and not something that was picked
|
||||
* arbitrarily. This value is returned whenever a uid that has no mapping in the
|
||||
* user namespace is returned to userspace:
|
||||
* https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/highuid.h?h=v4.4#n40
|
||||
*/
|
||||
#define AID_OVERFLOWUID 65534 /* unmapped user in the user namespace */
|
||||
|
||||
/* use the ranges below to determine whether a process is isolated */
|
||||
#define AID_ISOLATED_START 90000 /* start of uids for fully isolated sandboxed processes */
|
||||
#define AID_ISOLATED_END 99999 /* end of uids for fully isolated sandboxed processes */
|
||||
|
||||
#define AID_USER 100000 /* TODO: switch users over to AID_USER_OFFSET */
|
||||
#define AID_USER_OFFSET 100000 /* offset for uid ranges for each user */
|
||||
|
||||
/*
|
||||
* android_ids has moved to pwd/grp functionality.
|
||||
* If you need to add one, the structure is now
|
||||
* auto-generated based on the AID_ constraints
|
||||
* documented at the top of this header file.
|
||||
* Also see build/tools/fs_config for more details.
|
||||
*/
|
||||
15
module/jni/include/logging.hpp
Normal file
15
module/jni/include/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
|
||||
29
module/jni/include/mount_parser.hpp
Normal file
29
module/jni/include/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);
|
||||
392
module/jni/include/zygisk.hpp
Normal file
392
module/jni/include/zygisk.hpp
Normal file
@@ -0,0 +1,392 @@
|
||||
/* Copyright 2022-2023 John "topjohnwu" Wu
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
// This is the public API for Zygisk modules.
|
||||
// DO NOT MODIFY ANY CODE IN THIS HEADER.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <jni.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#define ZYGISK_API_VERSION 4
|
||||
|
||||
/*
|
||||
|
||||
***************
|
||||
* Introduction
|
||||
***************
|
||||
|
||||
On Android, all app processes are forked from a special daemon called "Zygote".
|
||||
For each new app process, zygote will fork a new process and perform "specialization".
|
||||
This specialization operation enforces the Android security sandbox on the newly forked
|
||||
process to make sure that 3rd party application code is only loaded after it is being
|
||||
restricted within a sandbox.
|
||||
|
||||
On Android, there is also this special process called "system_server". This single
|
||||
process hosts a significant portion of system services, which controls how the
|
||||
Android operating system and apps interact with each other.
|
||||
|
||||
The Zygisk framework provides a way to allow developers to build modules and run custom
|
||||
code before and after system_server and any app processes' specialization.
|
||||
This enable developers to inject code and alter the behavior of system_server and app processes.
|
||||
|
||||
Please note that modules will only be loaded after zygote has forked the child process.
|
||||
THIS MEANS ALL OF YOUR CODE RUNS IN THE APP/SYSTEM_SERVER PROCESS, NOT THE ZYGOTE DAEMON!
|
||||
|
||||
*********************
|
||||
* Development Guide
|
||||
*********************
|
||||
|
||||
Define a class and inherit zygisk::ModuleBase to implement the functionality of your module.
|
||||
Use the macro REGISTER_ZYGISK_MODULE(className) to register that class to Zygisk.
|
||||
|
||||
Example code:
|
||||
|
||||
static jint (*orig_logger_entry_max)(JNIEnv *env);
|
||||
static jint my_logger_entry_max(JNIEnv *env) { return orig_logger_entry_max(env); }
|
||||
|
||||
class ExampleModule : public zygisk::ModuleBase {
|
||||
public:
|
||||
void onLoad(zygisk::Api *api, JNIEnv *env) override {
|
||||
this->api = api;
|
||||
this->env = env;
|
||||
}
|
||||
void preAppSpecialize(zygisk::AppSpecializeArgs *args) override {
|
||||
JNINativeMethod methods[] = {
|
||||
{ "logger_entry_max_payload_native", "()I", (void*) my_logger_entry_max },
|
||||
};
|
||||
api->hookJniNativeMethods(env, "android/util/Log", methods, 1);
|
||||
*(void **) &orig_logger_entry_max = methods[0].fnPtr;
|
||||
}
|
||||
private:
|
||||
zygisk::Api *api;
|
||||
JNIEnv *env;
|
||||
};
|
||||
|
||||
REGISTER_ZYGISK_MODULE(ExampleModule)
|
||||
|
||||
-----------------------------------------------------------------------------------------
|
||||
|
||||
Since your module class's code runs with either Zygote's privilege in pre[XXX]Specialize,
|
||||
or runs in the sandbox of the target process in post[XXX]Specialize, the code in your class
|
||||
never runs in a true superuser environment.
|
||||
|
||||
If your module require access to superuser permissions, you can create and register
|
||||
a root companion handler function. This function runs in a separate root companion
|
||||
daemon process, and an Unix domain socket is provided to allow you to perform IPC between
|
||||
your target process and the root companion process.
|
||||
|
||||
Example code:
|
||||
|
||||
static void example_handler(int socket) { ... }
|
||||
|
||||
REGISTER_ZYGISK_COMPANION(example_handler)
|
||||
|
||||
*/
|
||||
|
||||
namespace zygisk {
|
||||
|
||||
struct Api;
|
||||
struct AppSpecializeArgs;
|
||||
struct ServerSpecializeArgs;
|
||||
|
||||
class ModuleBase {
|
||||
public:
|
||||
|
||||
// This method is called as soon as the module is loaded into the target process.
|
||||
// A Zygisk API handle will be passed as an argument.
|
||||
virtual void onLoad([[maybe_unused]] Api *api, [[maybe_unused]] JNIEnv *env) {}
|
||||
|
||||
// This method is called before the app process is specialized.
|
||||
// At this point, the process just got forked from zygote, but no app specific specialization
|
||||
// is applied. This means that the process does not have any sandbox restrictions and
|
||||
// still runs with the same privilege of zygote.
|
||||
//
|
||||
// All the arguments that will be sent and used for app specialization is passed as a single
|
||||
// AppSpecializeArgs object. You can read and overwrite these arguments to change how the app
|
||||
// process will be specialized.
|
||||
//
|
||||
// If you need to run some operations as superuser, you can call Api::connectCompanion() to
|
||||
// get a socket to do IPC calls with a root companion process.
|
||||
// See Api::connectCompanion() for more info.
|
||||
virtual void preAppSpecialize([[maybe_unused]] AppSpecializeArgs *args) {}
|
||||
|
||||
// This method is called after the app process is specialized.
|
||||
// At this point, the process has all sandbox restrictions enabled for this application.
|
||||
// This means that this method runs with the same privilege of the app's own code.
|
||||
virtual void postAppSpecialize([[maybe_unused]] const AppSpecializeArgs *args) {}
|
||||
|
||||
// This method is called before the system server process is specialized.
|
||||
// See preAppSpecialize(args) for more info.
|
||||
virtual void preServerSpecialize([[maybe_unused]] ServerSpecializeArgs *args) {}
|
||||
|
||||
// This method is called after the system server process is specialized.
|
||||
// At this point, the process runs with the privilege of system_server.
|
||||
virtual void postServerSpecialize([[maybe_unused]] const ServerSpecializeArgs *args) {}
|
||||
};
|
||||
|
||||
struct AppSpecializeArgs {
|
||||
// Required arguments. These arguments are guaranteed to exist on all Android versions.
|
||||
jint &uid;
|
||||
jint &gid;
|
||||
jintArray &gids;
|
||||
jint &runtime_flags;
|
||||
jobjectArray &rlimits;
|
||||
jint &mount_external;
|
||||
jstring &se_info;
|
||||
jstring &nice_name;
|
||||
jstring &instruction_set;
|
||||
jstring &app_data_dir;
|
||||
|
||||
// Optional arguments. Please check whether the pointer is null before de-referencing
|
||||
jintArray *const fds_to_ignore;
|
||||
jboolean *const is_child_zygote;
|
||||
jboolean *const is_top_app;
|
||||
jobjectArray *const pkg_data_info_list;
|
||||
jobjectArray *const whitelisted_data_info_list;
|
||||
jboolean *const mount_data_dirs;
|
||||
jboolean *const mount_storage_dirs;
|
||||
|
||||
AppSpecializeArgs() = delete;
|
||||
};
|
||||
|
||||
struct ServerSpecializeArgs {
|
||||
jint &uid;
|
||||
jint &gid;
|
||||
jintArray &gids;
|
||||
jint &runtime_flags;
|
||||
jlong &permitted_capabilities;
|
||||
jlong &effective_capabilities;
|
||||
|
||||
ServerSpecializeArgs() = delete;
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
struct api_table;
|
||||
template <class T> void entry_impl(api_table *, JNIEnv *);
|
||||
}
|
||||
|
||||
// These values are used in Api::setOption(Option)
|
||||
enum Option : int {
|
||||
// Force Magisk's denylist unmount routines to run on this process.
|
||||
//
|
||||
// Setting this option only makes sense in preAppSpecialize.
|
||||
// The actual unmounting happens during app process specialization.
|
||||
//
|
||||
// Set this option to force all Magisk and modules' files to be unmounted from the
|
||||
// mount namespace of the process, regardless of the denylist enforcement status.
|
||||
FORCE_DENYLIST_UNMOUNT = 0,
|
||||
|
||||
// When this option is set, your module's library will be dlclose-ed after post[XXX]Specialize.
|
||||
// Be aware that after dlclose-ing your module, all of your code will be unmapped from memory.
|
||||
// YOU MUST NOT ENABLE THIS OPTION AFTER HOOKING ANY FUNCTIONS IN THE PROCESS.
|
||||
DLCLOSE_MODULE_LIBRARY = 1,
|
||||
};
|
||||
|
||||
// Bit masks of the return value of Api::getFlags()
|
||||
enum StateFlag : uint32_t {
|
||||
// The user has granted root access to the current process
|
||||
PROCESS_GRANTED_ROOT = (1u << 0),
|
||||
|
||||
// The current process was added on the denylist
|
||||
PROCESS_ON_DENYLIST = (1u << 1),
|
||||
};
|
||||
|
||||
// All API methods will stop working after post[XXX]Specialize as Zygisk will be unloaded
|
||||
// from the specialized process afterwards.
|
||||
struct Api {
|
||||
|
||||
// Connect to a root companion process and get a Unix domain socket for IPC.
|
||||
//
|
||||
// This API only works in the pre[XXX]Specialize methods due to SELinux restrictions.
|
||||
//
|
||||
// The pre[XXX]Specialize methods run with the same privilege of zygote.
|
||||
// If you would like to do some operations with superuser permissions, register a handler
|
||||
// function that would be called in the root process with REGISTER_ZYGISK_COMPANION(func).
|
||||
// Another good use case for a companion process is that if you want to share some resources
|
||||
// across multiple processes, hold the resources in the companion process and pass it over.
|
||||
//
|
||||
// The root companion process is ABI aware; that is, when calling this method from a 32-bit
|
||||
// process, you will be connected to a 32-bit companion process, and vice versa for 64-bit.
|
||||
//
|
||||
// Returns a file descriptor to a socket that is connected to the socket passed to your
|
||||
// module's companion request handler. Returns -1 if the connection attempt failed.
|
||||
int connectCompanion();
|
||||
|
||||
// Get the file descriptor of the root folder of the current module.
|
||||
//
|
||||
// This API only works in the pre[XXX]Specialize methods.
|
||||
// Accessing the directory returned is only possible in the pre[XXX]Specialize methods
|
||||
// or in the root companion process (assuming that you sent the fd over the socket).
|
||||
// Both restrictions are due to SELinux and UID.
|
||||
//
|
||||
// Returns -1 if errors occurred.
|
||||
int getModuleDir();
|
||||
|
||||
// Set various options for your module.
|
||||
// Please note that this method accepts one single option at a time.
|
||||
// Check zygisk::Option for the full list of options available.
|
||||
void setOption(Option opt);
|
||||
|
||||
// Get information about the current process.
|
||||
// Returns bitwise-or'd zygisk::StateFlag values.
|
||||
uint32_t getFlags();
|
||||
|
||||
// Exempt the provided file descriptor from being automatically closed.
|
||||
//
|
||||
// This API only make sense in preAppSpecialize; calling this method in any other situation
|
||||
// is either a no-op (returns true) or an error (returns false).
|
||||
//
|
||||
// When false is returned, the provided file descriptor will eventually be closed by zygote.
|
||||
bool exemptFd(int fd);
|
||||
|
||||
// Hook JNI native methods for a class
|
||||
//
|
||||
// Lookup all registered JNI native methods and replace it with your own methods.
|
||||
// The original function pointer will be saved in each JNINativeMethod's fnPtr.
|
||||
// If no matching class, method name, or signature is found, that specific JNINativeMethod.fnPtr
|
||||
// will be set to nullptr.
|
||||
void hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int numMethods);
|
||||
|
||||
// Hook functions in the PLT (Procedure Linkage Table) of ELFs loaded in memory.
|
||||
//
|
||||
// Parsing /proc/[PID]/maps will give you the memory map of a process. As an example:
|
||||
//
|
||||
// <address> <perms> <offset> <dev> <inode> <pathname>
|
||||
// 56b4346000-56b4347000 r-xp 00002000 fe:00 235 /system/bin/app_process64
|
||||
// (More details: https://man7.org/linux/man-pages/man5/proc.5.html)
|
||||
//
|
||||
// The `dev` and `inode` pair uniquely identifies a file being mapped into memory.
|
||||
// For matching ELFs loaded in memory, replace function `symbol` with `newFunc`.
|
||||
// If `oldFunc` is not nullptr, the original function pointer will be saved to `oldFunc`.
|
||||
void pltHookRegister(dev_t dev, ino_t inode, const char *symbol, void *newFunc, void **oldFunc);
|
||||
|
||||
// Commit all the hooks that was previously registered.
|
||||
// Returns false if an error occurred.
|
||||
bool pltHookCommit();
|
||||
|
||||
private:
|
||||
internal::api_table *tbl;
|
||||
template <class T> friend void internal::entry_impl(internal::api_table *, JNIEnv *);
|
||||
};
|
||||
|
||||
// Register a class as a Zygisk module
|
||||
|
||||
#define REGISTER_ZYGISK_MODULE(clazz) \
|
||||
void zygisk_module_entry(zygisk::internal::api_table *table, JNIEnv *env) { \
|
||||
zygisk::internal::entry_impl<clazz>(table, env); \
|
||||
}
|
||||
|
||||
// Register a root companion request handler function for your module
|
||||
//
|
||||
// The function runs in a superuser daemon process and handles a root companion request from
|
||||
// your module running in a target process. The function has to accept an integer value,
|
||||
// which is a Unix domain socket that is connected to the target process.
|
||||
// See Api::connectCompanion() for more info.
|
||||
//
|
||||
// NOTE: the function can run concurrently on multiple threads.
|
||||
// Be aware of race conditions if you have globally shared resources.
|
||||
|
||||
#define REGISTER_ZYGISK_COMPANION(func) \
|
||||
void zygisk_companion_entry(int client) { func(client); }
|
||||
|
||||
/*********************************************************
|
||||
* The following is internal ABI implementation detail.
|
||||
* You do not have to understand what it is doing.
|
||||
*********************************************************/
|
||||
|
||||
namespace internal {
|
||||
|
||||
struct module_abi {
|
||||
long api_version;
|
||||
ModuleBase *impl;
|
||||
|
||||
void (*preAppSpecialize)(ModuleBase *, AppSpecializeArgs *);
|
||||
void (*postAppSpecialize)(ModuleBase *, const AppSpecializeArgs *);
|
||||
void (*preServerSpecialize)(ModuleBase *, ServerSpecializeArgs *);
|
||||
void (*postServerSpecialize)(ModuleBase *, const ServerSpecializeArgs *);
|
||||
|
||||
module_abi(ModuleBase *module) : api_version(ZYGISK_API_VERSION), impl(module) {
|
||||
preAppSpecialize = [](auto m, auto args) { m->preAppSpecialize(args); };
|
||||
postAppSpecialize = [](auto m, auto args) { m->postAppSpecialize(args); };
|
||||
preServerSpecialize = [](auto m, auto args) { m->preServerSpecialize(args); };
|
||||
postServerSpecialize = [](auto m, auto args) { m->postServerSpecialize(args); };
|
||||
}
|
||||
};
|
||||
|
||||
struct api_table {
|
||||
// Base
|
||||
void *impl;
|
||||
bool (*registerModule)(api_table *, module_abi *);
|
||||
|
||||
void (*hookJniNativeMethods)(JNIEnv *, const char *, JNINativeMethod *, int);
|
||||
void (*pltHookRegister)(dev_t, ino_t, const char *, void *, void **);
|
||||
bool (*exemptFd)(int);
|
||||
bool (*pltHookCommit)();
|
||||
int (*connectCompanion)(void * /* impl */);
|
||||
void (*setOption)(void * /* impl */, Option);
|
||||
int (*getModuleDir)(void * /* impl */);
|
||||
uint32_t (*getFlags)(void * /* impl */);
|
||||
};
|
||||
|
||||
template <class T>
|
||||
void entry_impl(api_table *table, JNIEnv *env) {
|
||||
static Api api;
|
||||
api.tbl = table;
|
||||
static T module;
|
||||
ModuleBase *m = &module;
|
||||
static module_abi abi(m);
|
||||
if (!table->registerModule(table, &abi)) return;
|
||||
m->onLoad(&api, env);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
inline int Api::connectCompanion() {
|
||||
return tbl->connectCompanion ? tbl->connectCompanion(tbl->impl) : -1;
|
||||
}
|
||||
inline int Api::getModuleDir() {
|
||||
return tbl->getModuleDir ? tbl->getModuleDir(tbl->impl) : -1;
|
||||
}
|
||||
inline void Api::setOption(Option opt) {
|
||||
if (tbl->setOption) tbl->setOption(tbl->impl, opt);
|
||||
}
|
||||
inline uint32_t Api::getFlags() {
|
||||
return tbl->getFlags ? tbl->getFlags(tbl->impl) : 0;
|
||||
}
|
||||
inline bool Api::exemptFd(int fd) {
|
||||
return tbl->exemptFd != nullptr && tbl->exemptFd(fd);
|
||||
}
|
||||
inline void Api::hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int numMethods) {
|
||||
if (tbl->hookJniNativeMethods) tbl->hookJniNativeMethods(env, className, methods, numMethods);
|
||||
}
|
||||
inline void Api::pltHookRegister(dev_t dev, ino_t inode, const char *symbol, void *newFunc, void **oldFunc) {
|
||||
if (tbl->pltHookRegister) tbl->pltHookRegister(dev, inode, symbol, newFunc, oldFunc);
|
||||
}
|
||||
inline bool Api::pltHookCommit() {
|
||||
return tbl->pltHookCommit != nullptr && tbl->pltHookCommit();
|
||||
}
|
||||
|
||||
} // namespace zygisk
|
||||
|
||||
extern "C" {
|
||||
|
||||
[[gnu::visibility("default"), maybe_unused]]
|
||||
void zygisk_module_entry(zygisk::internal::api_table *, JNIEnv *);
|
||||
|
||||
[[gnu::visibility("default"), maybe_unused]]
|
||||
void zygisk_companion_entry(int);
|
||||
|
||||
} // extern "C"
|
||||
Reference in New Issue
Block a user