Magisk/native/jni/zygisk/api.hpp

333 lines
13 KiB
C++
Raw Normal View History

2021-10-05 10:53:11 +00:00
// All content of this file is released to the public domain.
2021-10-09 18:53:40 +00:00
// This file is the public API for Zygisk modules.
// DO NOT use this file for developing Zygisk modules as it might contain WIP changes.
// Always use the following header for development as those are finalized APIs:
2021-10-05 10:53:11 +00:00
// https://github.com/topjohnwu/zygisk-module-sample/blob/master/module/jni/zygisk.hpp
2021-08-12 05:56:18 +00:00
#pragma once
#include <jni.h>
2022-03-03 06:01:35 +00:00
#define ZYGISK_API_VERSION 3
2021-10-05 10:53:11 +00:00
/*
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.
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!
Example code:
static jint (*orig_logger_entry_max)(JNIEnv *env);
static jint my_logger_entry_max(JNIEnv *env) { return orig_logger_entry_max(env); }
2021-10-06 05:42:55 +00:00
static void example_handler(int socket) { ... }
2021-10-05 10:53:11 +00:00
class ExampleModule : public zygisk::ModuleBase {
public:
2021-10-06 05:42:55 +00:00
void onLoad(zygisk::Api *api, JNIEnv *env) override {
this->api = api;
2021-10-21 10:20:04 +00:00
this->env = env;
2021-10-05 10:53:11 +00:00
}
2021-10-06 05:42:55 +00:00
void preAppSpecialize(zygisk::AppSpecializeArgs *args) override {
2021-10-05 10:53:11 +00:00
JNINativeMethod methods[] = {
{ "logger_entry_max_payload_native", "()I", (void*) my_logger_entry_max },
};
2021-10-21 10:20:04 +00:00
api->hookJniNativeMethods(env, "android/util/Log", methods, 1);
2021-10-05 10:53:11 +00:00
*(void **) &orig_logger_entry_max = methods[0].fnPtr;
}
private:
2021-10-06 05:42:55 +00:00
zygisk::Api *api;
2021-10-21 10:20:04 +00:00
JNIEnv *env;
2021-10-05 10:53:11 +00:00
};
REGISTER_ZYGISK_MODULE(ExampleModule)
2021-10-06 05:42:55 +00:00
REGISTER_ZYGISK_COMPANION(example_handler)
2021-10-05 10:53:11 +00:00
*/
namespace zygisk {
struct Api;
struct AppSpecializeArgs;
struct ServerSpecializeArgs;
class ModuleBase {
public:
// This function is called when the module is loaded into the target process.
// A Zygisk API handle will be sent as an argument; call utility functions or interface
// with Zygisk through this handle.
2022-01-25 12:16:14 +00:00
virtual void onLoad([[maybe_unused]] Api *api, [[maybe_unused]] JNIEnv *env) {}
2021-10-05 10:53:11 +00:00
// This function 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.
2022-01-25 12:16:14 +00:00
virtual void preAppSpecialize([[maybe_unused]] AppSpecializeArgs *args) {}
2021-10-05 10:53:11 +00:00
// This function 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 function runs as the same privilege of the app's own code.
2022-01-25 12:16:14 +00:00
virtual void postAppSpecialize([[maybe_unused]] const AppSpecializeArgs *args) {}
2021-10-05 10:53:11 +00:00
// This function is called before the system server process is specialized.
// See preAppSpecialize(args) for more info.
2022-01-25 12:16:14 +00:00
virtual void preServerSpecialize([[maybe_unused]] ServerSpecializeArgs *args) {}
2021-10-05 10:53:11 +00:00
2021-10-17 12:42:33 +00:00
// This function is called after the system server process is specialized.
2021-10-05 10:53:11 +00:00
// At this point, the process runs with the privilege of system_server.
2022-01-25 12:16:14 +00:00
virtual void postServerSpecialize([[maybe_unused]] const ServerSpecializeArgs *args) {}
2021-10-05 10:53:11 +00:00
};
struct AppSpecializeArgs {
// Required arguments. These arguments are guaranteed to exist on all Android versions.
2021-08-12 05:56:18 +00:00
jint &uid;
jint &gid;
jintArray &gids;
jint &runtime_flags;
2022-03-03 06:01:35 +00:00
jobjectArray &rlimits;
2021-08-12 05:56:18 +00:00
jint &mount_external;
jstring &se_info;
jstring &nice_name;
jstring &instruction_set;
jstring &app_data_dir;
2021-10-05 10:53:11 +00:00
// Optional arguments. Please check whether the pointer is null before de-referencing
2022-03-03 06:01:35 +00:00
jintArray *const fds_to_ignore;
2021-10-05 10:53:11 +00:00
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;
2021-08-12 05:56:18 +00:00
};
2021-10-05 10:53:11 +00:00
struct ServerSpecializeArgs {
2021-08-12 05:56:18 +00:00
jint &uid;
jint &gid;
jintArray &gids;
jint &runtime_flags;
jlong &permitted_capabilities;
jlong &effective_capabilities;
2021-10-05 10:53:11 +00:00
ServerSpecializeArgs() = delete;
2021-08-12 05:56:18 +00:00
};
2021-10-05 10:53:11 +00:00
namespace internal {
struct api_table;
2021-10-06 05:42:55 +00:00
template <class T> void entry_impl(api_table *, JNIEnv *);
2021-10-05 10:53:11 +00:00
}
2021-10-21 10:20:04 +00:00
// 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.
//
2022-01-18 03:54:33 +00:00
// 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.
2021-10-21 10:20:04 +00:00
FORCE_DENYLIST_UNMOUNT = 0,
// When this option is set, your module's library will be dlclose-ed after post[XXX]Specialize.
2022-01-18 03:54:33 +00:00
// 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.
2021-10-21 10:20:04 +00:00
DLCLOSE_MODULE_LIBRARY = 1,
};
2022-01-18 03:54:33 +00:00
// 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),
};
2021-11-07 21:05:44 +00:00
// All API functions will stop working after post[XXX]Specialize as Zygisk will be unloaded
// from the specialized process afterwards.
2021-10-05 10:53:11 +00:00
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 functions due to SELinux restrictions.
//
// The pre[XXX]Specialize functions run with the same privilege of zygote.
2021-10-06 05:42:55 +00:00
// 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).
2021-10-05 10:53:11 +00:00
// 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.
//
2021-10-23 21:38:30 +00:00
// The root companion process is ABI aware; that is, when calling this function from a 32-bit
// process, you will be connected to a 32-bit companion process, and vice versa for 64-bit.
//
2021-10-17 11:36:18 +00:00
// 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.
2021-10-05 10:53:11 +00:00
int connectCompanion();
2022-01-14 11:10:02 +00:00
// Get the file descriptor of the root folder of the current module.
//
// This API only works in the pre[XXX]Specialize functions.
// Accessing the directory returned is only possible in the pre[XXX]Specialize functions
// 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();
2021-10-21 10:20:04 +00:00
// Set various options for your module.
// Please note that this function accepts one single option at a time.
// Check zygisk::Option for the full list of options available.
void setOption(Option opt);
2021-10-05 10:53:11 +00:00
2022-01-18 03:54:33 +00:00
// Get information about the current process.
// Returns bitwise-or'd zygisk::StateFlag values.
uint32_t getFlags();
2021-10-05 10:53:11 +00:00
// Hook JNI native methods for a class
//
// Lookup all registered JNI native methods and replace it with your own functions.
2021-10-06 05:42:55 +00:00
// The original function pointer will be saved in each JNINativeMethod's fnPtr.
2021-10-05 10:53:11 +00:00
// If no matching class, method name, or signature is found, that specific JNINativeMethod.fnPtr
// will be set to nullptr.
2021-10-13 11:52:02 +00:00
void hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int numMethods);
2021-10-05 10:53:11 +00:00
// For ELFs loaded in memory matching `regex`, replace function `symbol` with `newFunc`.
// If `oldFunc` is not nullptr, the original function pointer will be saved to `oldFunc`.
void pltHookRegister(const char *regex, const char *symbol, void *newFunc, void **oldFunc);
// For ELFs loaded in memory matching `regex`, exclude hooks registered for `symbol`.
2021-10-06 05:42:55 +00:00
// If `symbol` is nullptr, then all symbols will be excluded.
2021-10-05 10:53:11 +00:00
void pltHookExclude(const char *regex, const char *symbol);
// Commit all the hooks that was previously registered.
// Returns false if an error occurred.
bool pltHookCommit();
private:
internal::api_table *impl;
2021-10-09 02:17:31 +00:00
template <class T> friend void internal::entry_impl(internal::api_table *, JNIEnv *);
2021-10-05 10:53:11 +00:00
};
2021-10-06 05:42:55 +00:00
// Register a class as a Zygisk module
2021-10-05 10:53:11 +00:00
#define REGISTER_ZYGISK_MODULE(clazz) \
2021-10-06 05:42:55 +00:00
void zygisk_module_entry(zygisk::internal::api_table *table, JNIEnv *env) { \
zygisk::internal::entry_impl<clazz>(table, env); \
2021-10-05 10:53:11 +00:00
}
2021-10-06 05:42:55 +00:00
// 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 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 a globally shared resource.
#define REGISTER_ZYGISK_COMPANION(func) \
void zygisk_companion_entry(int client) { func(client); }
2021-10-05 10:53:11 +00:00
/************************************************************************************
* All the code after this point is internal code used to interface with Zygisk
* and guarantee ABI stability. You do not have to understand what it is doing.
************************************************************************************/
namespace internal {
struct module_abi {
long api_version;
ModuleBase *_this;
2021-10-06 05:42:55 +00:00
void (*preAppSpecialize)(ModuleBase *, AppSpecializeArgs *);
void (*postAppSpecialize)(ModuleBase *, const AppSpecializeArgs *);
void (*preServerSpecialize)(ModuleBase *, ServerSpecializeArgs *);
void (*postServerSpecialize)(ModuleBase *, const ServerSpecializeArgs *);
2021-10-05 10:53:11 +00:00
module_abi(ModuleBase *module) : api_version(ZYGISK_API_VERSION), _this(module) {
2021-10-06 05:42:55 +00:00
preAppSpecialize = [](auto self, auto args) { self->preAppSpecialize(args); };
postAppSpecialize = [](auto self, auto args) { self->postAppSpecialize(args); };
preServerSpecialize = [](auto self, auto args) { self->preServerSpecialize(args); };
postServerSpecialize = [](auto self, auto args) { self->postServerSpecialize(args); };
2021-10-05 10:53:11 +00:00
}
};
struct api_table {
// These first 2 entries are permanent, shall never change
void *_this;
bool (*registerModule)(api_table *, module_abi *);
// Utility functions
2021-10-13 11:52:02 +00:00
void (*hookJniNativeMethods)(JNIEnv *, const char *, JNINativeMethod *, int);
2021-10-05 10:53:11 +00:00
void (*pltHookRegister)(const char *, const char *, void *, void **);
void (*pltHookExclude)(const char *, const char *);
bool (*pltHookCommit)();
// Zygisk functions
int (*connectCompanion)(void * /* _this */);
2021-10-21 10:20:04 +00:00
void (*setOption)(void * /* _this */, Option);
2022-01-14 11:10:02 +00:00
int (*getModuleDir)(void * /* _this */);
2022-01-18 03:54:33 +00:00
uint32_t (*getFlags)(void * /* _this */);
2021-10-05 10:53:11 +00:00
};
template <class T>
2021-10-06 05:42:55 +00:00
void entry_impl(api_table *table, JNIEnv *env) {
2021-10-09 18:53:40 +00:00
ModuleBase *module = new T();
2021-10-05 10:53:11 +00:00
if (!table->registerModule(table, new module_abi(module)))
return;
auto api = new Api();
api->impl = table;
2021-10-06 05:42:55 +00:00
module->onLoad(api, env);
2021-10-05 10:53:11 +00:00
}
} // namespace internal
inline int Api::connectCompanion() {
2021-11-07 21:05:44 +00:00
return impl->connectCompanion ? impl->connectCompanion(impl->_this) : -1;
2022-01-14 11:10:02 +00:00
}
inline int Api::getModuleDir() {
return impl->getModuleDir ? impl->getModuleDir(impl->_this) : -1;
2021-10-05 10:53:11 +00:00
}
inline void Api::setOption(Option opt) {
2021-11-07 21:05:44 +00:00
if (impl->setOption) impl->setOption(impl->_this, opt);
2021-10-05 10:53:11 +00:00
}
2022-01-18 03:54:33 +00:00
inline uint32_t Api::getFlags() {
return impl->getFlags ? impl->getFlags(impl->_this) : 0;
}
inline void Api::hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int numMethods) {
2021-11-07 21:05:44 +00:00
if (impl->hookJniNativeMethods) impl->hookJniNativeMethods(env, className, methods, numMethods);
2021-10-05 10:53:11 +00:00
}
inline void Api::pltHookRegister(const char *regex, const char *symbol, void *newFunc, void **oldFunc) {
2021-11-07 21:05:44 +00:00
if (impl->pltHookRegister) impl->pltHookRegister(regex, symbol, newFunc, oldFunc);
2021-10-05 10:53:11 +00:00
}
inline void Api::pltHookExclude(const char *regex, const char *symbol) {
2021-11-07 21:05:44 +00:00
if (impl->pltHookExclude) impl->pltHookExclude(regex, symbol);
2021-10-05 10:53:11 +00:00
}
inline bool Api::pltHookCommit() {
2021-11-07 21:05:44 +00:00
return impl->pltHookCommit != nullptr && impl->pltHookCommit();
2021-10-05 10:53:11 +00:00
}
} // namespace zygisk
[[gnu::visibility("default")]] [[gnu::used]]
2021-10-06 05:42:55 +00:00
extern "C" void zygisk_module_entry(zygisk::internal::api_table *, JNIEnv *);
[[gnu::visibility("default")]] [[gnu::used]]
extern "C" void zygisk_companion_entry(int);