Update Zygisk API documentation

This commit is contained in:
topjohnwu 2023-02-26 03:16:13 -08:00
parent 79586ece4c
commit 85d9756f62

View File

@ -27,19 +27,39 @@
/* /*
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. * 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. 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! 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: Example code:
static jint (*orig_logger_entry_max)(JNIEnv *env); static jint (*orig_logger_entry_max)(JNIEnv *env);
static jint my_logger_entry_max(JNIEnv *env) { return orig_logger_entry_max(env); } static jint my_logger_entry_max(JNIEnv *env) { return orig_logger_entry_max(env); }
static void example_handler(int socket) { ... }
class ExampleModule : public zygisk::ModuleBase { class ExampleModule : public zygisk::ModuleBase {
public: public:
void onLoad(zygisk::Api *api, JNIEnv *env) override { void onLoad(zygisk::Api *api, JNIEnv *env) override {
@ -60,6 +80,21 @@ private:
REGISTER_ZYGISK_MODULE(ExampleModule) 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) REGISTER_ZYGISK_COMPANION(example_handler)
*/ */
@ -93,7 +128,7 @@ public:
// This method is called after the app process is specialized. // This method is called after the app process is specialized.
// At this point, the process has all sandbox restrictions enabled for this application. // At this point, the process has all sandbox restrictions enabled for this application.
// This means that this method runs as the same privilege of the app's own code. // This means that this method runs with the same privilege of the app's own code.
virtual void postAppSpecialize([[maybe_unused]] const AppSpecializeArgs *args) {} virtual void postAppSpecialize([[maybe_unused]] const AppSpecializeArgs *args) {}
// This method is called before the system server process is specialized. // This method is called before the system server process is specialized.
@ -228,7 +263,16 @@ struct Api {
// will be set to nullptr. // will be set to nullptr.
void hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int numMethods); void hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int numMethods);
// For ELFs loaded in memory matching `inode`, replace function `symbol` with `newFunc`. // 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`. // 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); void pltHookRegister(dev_t dev, ino_t inode, const char *symbol, void *newFunc, void **oldFunc);
@ -252,11 +296,11 @@ void zygisk_module_entry(zygisk::internal::api_table *table, JNIEnv *env) { \
// //
// The function runs in a superuser daemon process and handles a root companion request from // 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, // 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. // which is a Unix domain socket that is connected to the target process.
// See Api::connectCompanion() for more info. // See Api::connectCompanion() for more info.
// //
// NOTE: the function can run concurrently on multiple threads. // NOTE: the function can run concurrently on multiple threads.
// Be aware of race conditions if you have a globally shared resource. // Be aware of race conditions if you have globally shared resources.
#define REGISTER_ZYGISK_COMPANION(func) \ #define REGISTER_ZYGISK_COMPANION(func) \
void zygisk_companion_entry(int client) { func(client); } void zygisk_companion_entry(int client) { func(client); }