Module guide
ShizuSU provides a module mechanism that achieves the effect of modifying the system directory while maintaining the integrity of the system partition. This mechanism is commonly known as "systemless". ShizuSU's module system is based on Magic Mount (from SukiSU-Ultra) — no metamodule required, modules work out of the box.
The module mechanism of ShizuSU is almost the same as that of Magisk. If you're familiar with Magisk module development, developing ShizuSU modules is very similar. You can skip the introduction of modules below and just read Difference with Magisk.
MODULE SYSTEM BASED ON MAGIC MOUNT
ShizuSU's module system is based on Magic Mount (from SukiSU-Ultra). No metamodule is required to mount the system directory — modules that modify /system files work out of the box. See Module System: Magic Mount.
KPM kernel modules
KernelPatch Modules (KPM) allow loading modules directly at the kernel level, ideal for advanced kernel modifications and enhancements. ShizuSU has full KPM support (ported from Apatch). To use KPM, enable CONFIG_KPM=y when building the kernel.
WebUI
ShizuSU's modules support displaying interfaces and interacting with users. For more details, refer to the WebUI documentation.
Module Configuration
ShizuSU provides a built-in configuration system that allows modules to store persistent or temporary key-value settings. For more details, refer to the Module Configuration documentation.
BusyBox
ShizuSU ships with a feature-complete BusyBox binary (including full SELinux support). The executable is located at /data/adb/ksu/bin/busybox. ShizuSU's BusyBox supports runtime toggle-able "ASH Standalone Shell Mode". What this Standalone Mode means is that when running in the ash shell of BusyBox, every single command will directly use the applet within BusyBox, regardless of what is set as PATH. For example, commands like ls, rm, chmod will NOT use what is in PATH (in the case of Android by default it will be /system/bin/ls, /system/bin/rm, and /system/bin/chmod respectively), but will instead directly call internal BusyBox applets. This makes sure that scripts always run in a predictable environment and always have the full suite of commands no matter which Android version it is running on. To force a command not to use BusyBox, you have to call the executable with full paths.
Every single shell script running in the context of ShizuSU will be executed in BusyBox's ash shell with Standalone Mode enabled. For what is relevant to 3rd party developers, this includes all boot scripts and module installation scripts.
For those who want to use this Standalone Mode feature outside of ShizuSU, there are 2 ways to enable it:
- Set environment variable
ASH_STANDALONEto1
Example:ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script> - Toggle with command-line options:
/data/adb/ksu/bin/busybox sh -o standalone <script>
To make sure all subsequent sh shell executed also runs in Standalone Mode, option 1 is the preferred method (and this is what ShizuSU and the ShizuSU manager use internally) as environment variables are inherited down to child processes.
DIFFERENCE WITH MAGISK
ShizuSU's BusyBox is now using the binary file compiled directly from the Magisk project. Thanks to Magisk! Therefore, you don't need to worry about compatibility issues between BusyBox scripts in Magisk and ShizuSU, as they're exactly the same!
ShizuSU modules
A ShizuSU module is a folder placed in /data/adb/modules with the structure below:
/data/adb/modules
├── .
├── .
|
├── $MODID <--- The folder is named with the ID of the module
│ │
│ │ *** Module Identity ***
│ │
│ ├── module.prop <--- This file stores the metadata of the module
│ │
│ │ *** Main Contents ***
│ │
│ ├── system <--- This folder will be mounted if skip_mount does not exist
│ │ ├── ...
│ │ ├── ...
│ │ └── ...
│ │
│ │ *** Status Flags ***
│ │
│ ├── skip_mount <--- If exists, ShizuSU will NOT mount your system folder
│ ├── disable <--- If exists, the module will be disabled
│ ├── remove <--- If exists, the module will be removed next reboot
│ │
│ │ *** Optional Files ***
│ │
│ ├── post-fs-data.sh <--- This script will be executed in post-fs-data
│ ├── post-mount.sh <--- This script will be executed in post-mount
│ ├── service.sh <--- This script will be executed in late_start service
│ ├── boot-completed.sh <--- This script will be executed on boot completed
| ├── uninstall.sh <--- This script will be executed when ShizuSU removes your module
| ├── action.sh <--- This script will be executed when user click the Action button in ShizuSU app
│ ├── system.prop <--- Properties in this file will be loaded as system properties by resetprop
│ ├── sepolicy.rule <--- Additional custom sepolicy rules
│ ├── initrc/ <--- .rc files in this directory will be injected into init.rc on boot
│ │ ├── myservice.rc
│ │ └── ...
│ │
│ │ *** Auto Generated, DO NOT MANUALLY CREATE OR MODIFY ***
│ │
│ ├── vendor <--- A symlink to $MODID/system/vendor
│ ├── product <--- A symlink to $MODID/system/product
│ ├── system_ext <--- A symlink to $MODID/system/system_ext
│ │
│ │ *** Any additional files / folders are allowed ***
│ │
│ ├── ...
│ └── ...
|
├── another_module
│ ├── .
│ └── .
├── .
├── .DIFFERENCE WITH MAGISK
ShizuSU doesn't have built-in support for Zygisk, so there is no content related to Zygisk in the module. However, you can use ZygiskNext to support Zygisk modules. In this case, the content of the Zygisk module is identical to that supported by Magisk.
module.prop
module.prop is a configuration file for a module. In ShizuSU, if a module doesn't contain this file, it won't be recognized as a module. The format of this file is as follows:
id=<string>
name=<string>
version=<string>
versionCode=<int>
author=<string>
description=<string>
updateJson=<url> (optional)
actionIcon=<path> (optional)
webuiIcon=<path> (optional)idhas to match this regular expression:^[a-zA-Z][a-zA-Z0-9._-]+$
Example: ✓a_module, ✓a.module, ✓module-101, ✗a module, ✗1_module, ✗-a-module
This is the unique identifier of your module. You should not change it once published.versionCodehas to be an integer. This is used to compare versions.- Others that were not mentioned above can be any single line string.
- Make sure to use the
UNIX (LF)line break type and not theWindows (CR+LF)orMacintosh (CR). actionIconandwebuiIconare optional icon paths used as the default icons for the module action shortcut and WebUI shortcut in the Manager. These paths must be relative to the module root directory. For example,actionIcon=icon/icon.pngwill be resolved as<MODDIR>/icon/icon.png.
DYNAMIC DESCRIPTION
The description field can be dynamically overridden at runtime using the module configuration system. See Overriding Module Description for details.
Shell scripts
Please read the Boot scripts section to understand the difference between post-fs-data.sh and service.sh. For most module developers, service.sh should be good enough if you just need to run a boot script, if you need to run the script after boot completed, please use boot-completed.sh. If you want to do something after module mounting, please use post-mount.sh.
In all scripts of your module, please use MODDIR=${0%/*} to get your module's base directory path; do NOT hardcode your module path in scripts.
DIFFERENCE WITH MAGISK
You can use the environment variable KSU to determine if a script is running in ShizuSU or Magisk. If running in ShizuSU, this value will be set to true.
system directory
The contents of this directory will be overlaid on top of the system's /system partition via Magic Mount (bind mount) after the system is booted. This means that:
- Files with the same name as those in the corresponding directory in the system will be overwritten by the files in this directory.
- Folders with the same name as those in the corresponding directory in the system will be merged with the folders in this directory.
If you want to delete a file or folder in the original system directory, you can declare a variable named REMOVE containing a list of directories in customize.sh. ShizuSU will hide these files via Magic Mount (the /system partition isn't actually changed). For example:
REMOVE="
/system/app/YouTube
/system/app/Bloatware
"/system/app/YouTube and /system/app/Bloatware will be hidden after the module takes effect.
If you want to replace a directory in the system, you can declare a variable named REPLACE in your customize.sh file, which includes a list of directories to be replaced. ShizuSU will bind-mount an empty directory over the target path, replacing it entirely. For example:
REPLACE="
/system/app/YouTube
/system/app/Bloatware
"/system/app/YouTube and /system/app/Bloatware will be replaced with empty directories after the module takes effect.
DIFFERENCE WITH OFFICIAL KernelSU
Official KernelSU uses an OverlayFS (metamodule) mechanism for systemless modifications, while ShizuSU, based on SukiSU-Ultra, uses Magic Mount (bind mount) just like Magisk. Both approaches share the same goal: modifying /system files without physically modifying the /system partition. ShizuSU uses Magic Mount only — there are no two module systems. See Module System: Magic Mount.
system.prop
This file follows the same format as build.prop. Each line comprises of [key]=[value].
sepolicy.rule
If your module requires some additional sepolicy patches, please add those rules into this file. Each line in this file will be treated as a policy statement.
initrc Injection
ShizuSU provides a mechanism to inject custom Android Init RC directives into the system's init.rc. This allows modules to register custom Android services, set property triggers, or perform other Init language actions without modifying the system partition.
During boot, the ShizuSU kernel module intercepts read() and fstat() system calls. When the Android init process reads /system/etc/init/hw/init.rc, ShizuSU transparently appends the custom RC content to the end of the file. The init process parses these injected directives just like the original init.rc content.
On the userspace side, ksud concatenates all .rc files from enabled modules into a single modules.rc file, stored on the /metadata partition. This file is automatically regenerated whenever a module's state changes (install, enable, disable, uninstall, etc.).
Module initrc Files
Create an initrc/ subdirectory in your module directory and place your .rc files there:
/data/adb/modules/<MODID>/
├── initrc/
│ ├── myservice.rc
│ └── another.rc
└── ...TIP
- Files must have an
.rcextension. - As long as the module is enabled, all
.rcfiles in theinitrc/directory will be included (executable permission is not required). - Files are processed in alphabetical order of file name within the directory, and modules are processed in alphabetical order of module ID.
General initrc Files
In addition to module-level RC files, you can place .rc files in the global directory:
/data/adb/initrc.d/
├── myservice.rc
└── another.rcGeneral initrc files require executable permission
Unlike the module initrc/ directory, files in /data/adb/initrc.d/ must have executable permissions to be included. Non-executable .rc files will be silently skipped.
General initrc.d/ files are processed before any module RC files.
Example
Here is an example .rc file that registers a custom Android service:
service myservice /data/adb/modules/mymodule/bin/myservice
user root
group root
disabled
seclabel u:r:ksu:s0
on property:sys.boot_completed=1
start myserviceIf this file is placed at /data/adb/modules/mymodule/initrc/myservice.rc, it will register a service named myservice at boot and start it when sys.boot_completed=1 is reached.
Manual Refresh
You can manually trigger the regeneration of modules.rc with the following command (changes take effect on the next boot):
ksud initrc refreshTIP
- initrc injection happens extremely early in the boot process (when init reads init.rc), before post-fs-data and any module scripts are executed.
- The injected RC content is treated by init as part of the original init.rc, supporting all Android Init language syntax (service definitions, triggers, property settings, etc.).
- initrc injection is not available in late-load mode, as the system call hooks are not installed in that mode.
- Module RC injection can be disabled by passing the
--no-custom-rcparameter when patching the image with ksud.
Module installer
A ShizuSU module installer is a ShizuSU module packaged in a ZIP file that can be flashed in the ShizuSU manager. The simplest ShizuSU module installer is just a ShizuSU module packed as a ZIP file.
module.zip
│
├── customize.sh <--- (Optional, more details later)
│ This script will be sourced by update-binary
├── ...
├── ... /* The rest of module's files */
│WARNING
ShizuSU module is NOT compatible for installation in a custom Recovery!
Customization
If you need to customize the module installation process, optionally you can create a script in the installer named customize.sh. This script will be sourced (not executed) by the module installer script after all files are extracted and default permissions and secontext are applied. This is very useful if your module requires additional setup based on the device ABI, or you need to set special permissions/secontext for some of your module files.
If you would like to fully control and customize the installation process, declare SKIPUNZIP=1 in customize.sh to skip all default installation steps. By doing so, your customize.sh will be responsible to install everything by itself.
The customize.sh script runs in ShizuSU's BusyBox ash shell with Standalone Mode enabled. The following variables and functions are available:
Variables
KSU(bool): a variable to mark that the script is running in the ShizuSU environment, and the value of this variable will always be true. You can use it to distinguish between ShizuSU and Magisk.KSU_VER(string): the version string of currently installed ShizuSU (e.g.v0.4.0).KSU_VER_CODE(int): the version code of currently installed ShizuSU in userspace (e.g.10672).KSU_KERNEL_VER_CODE(int): the version code of currently installed ShizuSU in kernel space (e.g.10672).BOOTMODE(bool): always betruein ShizuSU.MODPATH(path): the path where your module files should be installed.TMPDIR(path): a place where you can temporarily store files.ZIPFILE(path): your module's installation ZIP.ARCH(string): the CPU architecture of the device. Value is eitherarm,arm64,x86, orx64.IS64BIT(bool):trueif$ARCHis eitherarm64orx64.API(int): the API level (Android version) of the device (e.g.,23for Android 6.0).KSU_UAPI_VER(int): the UAPI version of ShizuSU userspace (ksud) (e.g.,2). This version is incremented when there are breaking changes in the kernel driver, and can be used by modules to check compatibility.KSU_RUNTIME_MODE(string): the current ShizuSU runtime mode. Possible values arebuilt-in(a.k.a. GKI mode, compiled into the kernel),lkm(loaded as a kernel module at boot), orlate-load(loaded as a kernel module after boot).KSU_LATE_LOAD(int?): if ShizuSU is late-loaded after boot, this variable is set to1; otherwise it is not set.
WARNING
In ShizuSU, MAGISK_VER_CODE is always 25200, and MAGISK_VER is always v25.2. Please don't use these two variables to determine whether ShizuSU is running or not.
Functions
ui_print <msg>
print <msg> to console
Avoid using 'echo' as it will not display in custom recovery's console
abort <msg>
print error message <msg> to console and terminate the installation
Avoid using 'exit' as it will skip the termination cleanup steps
set_perm <target> <owner> <group> <permission> [context]
if [context] is not set, the default is "u:object_r:system_file:s0"
this function is a shorthand for the following commands:
chown owner.group target
chmod permission target
chcon context target
set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
if [context] is not set, the default is "u:object_r:system_file:s0"
for all files in <directory>, it will call:
set_perm file owner group filepermission context
for all directories in <directory> (including itself), it will call:
set_perm dir owner group dirpermission contextBoot scripts
In ShizuSU, scripts are divided into two types based on their running mode: post-fs-data mode and late_start service mode.
- post-fs-data mode
- This stage is BLOCKING. The boot process is paused before execution is done or after 10 seconds.
- Scripts run before any modules are mounted. This allows a module developer to dynamically adjust their modules before it gets mounted.
- This stage happens before Zygote is started, which pretty much means everything in Android.
- WARNING: Using
setpropwill deadlock the boot process! Please useresetprop -n <prop_name> <prop_value>instead. - Only run scripts in this mode if necessary.
- late_start service mode
- This stage is NON-BLOCKING. Your script runs in parallel with the rest of the booting process.
- This is the recommended stage to run most scripts.
In ShizuSU, startup scripts are divided into two types based on their storage location: general scripts and module scripts.
- General scripts
- Placed in
/data/adb/post-fs-data.d,/data/adb/service.d,/data/adb/post-mount.dor/data/adb/boot-completed.d. - Only executed if the script is set as executable (
chmod +x script.sh). - Scripts in
post-fs-data.druns in post-fs-data mode, and scripts inservice.druns in late_start service mode. - Modules should NOT add general scripts during installation.
- Placed in
- Module scripts
- Placed in the module's own folder.
- Only executed if the module is enabled.
post-fs-data.shruns in post-fs-data mode,service.shruns in late_start service mode,boot-completed.shruns on boot completed,post-mount.shruns on module mounting complete.
All boot scripts will run in ShizuSU's BusyBox ash shell with Standalone Mode enabled.
Boot scripts process explanation
The following is the relevant boot process for Android (some parts are omitted), which includes the operation of ShizuSU (with leading asterisks), and can help you better understand the purpose of these module scripts:
0. Bootloader (nothing on screen)
load patched boot.img
load kernel:
- GKI mode: GKI kernel with ShizuSU integrated
- LKM mode: stock kernel
...
1. kernel exec init (OEM logo on screen):
- GKI mode: stock init
- LKM mode: exec ksuinit, insmod kernelsu.ko, exec stock init
mount /dev, /dev/pts, /proc, /sys, etc.
property-init -> read default props
read init.rc
*initrc injection: Kernel hook appends ShizuSU core RC and module modules.rc to init.rc
...
early-init -> init -> late_init
early-fs
start vold
fs
mount /vendor, /system, /persist, etc.
post-fs-data
*safe mode check
*execute general scripts in post-fs-data.d/
*load sepolicy.rule
*execute module-mounting post-fs-data script
*execute module scripts post-fs-data.sh
**(Zygisk)./bin/zygisk-ptrace64 monitor
*(pre)load system.prop (same as resetprop -n)
*execute Magic Mount metamount.sh (mounts all modules)
*execute general scripts in post-mount.d/
*execute module-mounting post-mount script
*execute module scripts post-mount.sh
zygote-start
load_all_props_action
*execute resetprop (actual set props for resetprop with -n option)
... -> boot
class_start core
start-service logd, console, vold, etc.
class_start main
start-service adb, netd (iptables), zygote, etc.
2. kernel2user init (ROM animation on screen, start by service bootanim)
*execute general scripts in service.d/
*execute module service script
*execute module scripts service.sh
*set props for resetprop without -p option
**(Zygisk) hook zygote (start zygiskd)
**(Zygisk) mount zygisksu/module.prop
start system apps (autostart)
...
boot complete (broadcast ACTION_BOOT_COMPLETED event)
*execute general scripts in boot-completed.d/
*execute module boot-completed script
*execute module scripts boot-completed.sh
3. User operable (lock screen)
input password to decrypt /data/data
*actual set props for resetprop with -p option
start user apps (autostart)If you're interested in Android Init Language, it's recommended to read its documentation.
Late-load mode
In addition to the standard boot flow described above, ShizuSU supports a late-load mode for LKM (Loadable Kernel Module) scenarios. In this mode, the ShizuSU kernel module is loaded after the system has fully booted, rather than during the init process.
When does late-load happen?
Late-load is triggered by running the ksud late-load command. This command:
- Detects the current KMI version and loads the corresponding
kernelsu.kofrom embedded assets. - Performs module initialization (SELinux rules, allowlist, features, etc.) that would normally happen during boot.
Since the system is already fully running, certain boot-time mechanisms are unavailable or unnecessary.
Differences from standard boot
| Behavior | Standard boot | Late-load mode |
|---|---|---|
| Kernel module loaded by init (PID 1) | Yes | No (loaded after boot) |
initrc injection (module .rc files to init.rc) | Yes | Unavailable |
| kprobe hooks for ksud (execve/read/fstat/input) | Yes | Skipped |
| Safe mode detection (volume key) | Yes | Always disabled |
| Boot log capture (logcat/dmesg) | Yes | Skipped |
| Magisk coexistence check | Yes | Skipped |
post-fs-data event reported to kernel | Yes | Skipped |
boot-completed event reported to kernel | Yes | Set directly during init |
post-fs-data.sh / post-fs-data.d/ scripts | Yes | Replaced by late-load stage |
system.prop loading | Yes | Yes |
| Magic Mount module mounting | Yes | Yes |
post-mount.sh / post-mount.d/ scripts | Yes | Yes |
service.sh / service.d/ scripts | Yes | Yes |
boot-completed.sh / boot-completed.d/ scripts | Yes | Yes |
KSU_LATE_LOAD environment variable | Not set | Set to 1 |
Kernel info flag 0x4 | Not set | Set |
Script execution order
In late-load mode, the script execution order is:
ksud late-load:
1. Load kernelsu.ko (if not already loaded)
2. Extract binaries, handle module updates, load SELinux rules, init features
3. Execute late-load.d/ and module late-load scripts (blocking)
4. Load system.prop (resetprop -n)
5. Execute Magic Mount module mounting
6. Execute post-mount.d/ and module post-mount.sh (blocking)
7. Execute service.d/ and module service.sh (non-blocking)
8. Execute boot-completed.d/ and module boot-completed.sh (non-blocking)Late-load specific scripts
Modules can provide a late-load.sh script that runs only in late-load mode, as a replacement for post-fs-data.sh. This script runs before module mounting, similar to post-fs-data.sh in the standard flow.
Additionally, general scripts can be placed in /data/adb/late-load.d/ to run during this stage.
Detecting late-load mode in scripts
Modules can detect late-load mode by checking the KSU_LATE_LOAD environment variable:
if [ "$KSU_LATE_LOAD" = "1" ]; then
# Running in late-load mode
echo "Late-load mode detected"
fiThis allows modules to adjust their behavior accordingly, for example skipping operations that are only needed during early boot.
Module Convenience
ShizuSU provides a set of module-management conveniences:
- Backup & restore: one-tap backup/restore of installed modules and the root allowlist.
- Batch installation: install multiple module zips at once; a single failure does not abort the whole flow — failures are collected and reported at the end.
- Batch management: one-tap enable, disable, disable-all, and uninstall-all.
