Skip to content

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:

  1. Set environment variable ASH_STANDALONE to 1
    Example: ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>
  2. 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:

txt
/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:

txt
id=<string>
name=<string>
version=<string>
versionCode=<int>
author=<string>
description=<string>
updateJson=<url> (optional)
actionIcon=<path> (optional)
webuiIcon=<path> (optional)
  • id has 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.
  • versionCode has 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 the Windows (CR+LF) or Macintosh (CR).
  • actionIcon and webuiIcon are 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.png will 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:

  1. Files with the same name as those in the corresponding directory in the system will be overwritten by the files in this directory.
  2. 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:

sh
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:

sh
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:

txt
/data/adb/modules/<MODID>/
├── initrc/
│   ├── myservice.rc
│   └── another.rc
└── ...

TIP

  • Files must have an .rc extension.
  • As long as the module is enabled, all .rc files in the initrc/ 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:

txt
/data/adb/initrc.d/
├── myservice.rc
└── another.rc

General 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:

rc
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 myservice

If 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):

sh
ksud initrc refresh

TIP

  • 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-rc parameter 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.

txt
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 be true in 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 either arm, arm64, x86, or x64.
  • IS64BIT (bool): true if $ARCH is either arm64 or x64.
  • API (int): the API level (Android version) of the device (e.g., 23 for 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 are built-in (a.k.a. GKI mode, compiled into the kernel), lkm (loaded as a kernel module at boot), or late-load (loaded as a kernel module after boot).
  • KSU_LATE_LOAD (int?): if ShizuSU is late-loaded after boot, this variable is set to 1; 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 ​

txt
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 context

Boot 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 setprop will deadlock the boot process! Please use resetprop -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.d or /data/adb/boot-completed.d.
    • Only executed if the script is set as executable (chmod +x script.sh).
    • Scripts in post-fs-data.d runs in post-fs-data mode, and scripts in service.d runs in late_start service mode.
    • Modules should NOT add general scripts during installation.
  • Module scripts
    • Placed in the module's own folder.
    • Only executed if the module is enabled.
    • post-fs-data.sh runs in post-fs-data mode, service.sh runs in late_start service mode, boot-completed.sh runs on boot completed, post-mount.sh runs 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:

txt
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:

  1. Detects the current KMI version and loads the corresponding kernelsu.ko from embedded assets.
  2. 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 ​

BehaviorStandard bootLate-load mode
Kernel module loaded by init (PID 1)YesNo (loaded after boot)
initrc injection (module .rc files to init.rc)YesUnavailable
kprobe hooks for ksud (execve/read/fstat/input)YesSkipped
Safe mode detection (volume key)YesAlways disabled
Boot log capture (logcat/dmesg)YesSkipped
Magisk coexistence checkYesSkipped
post-fs-data event reported to kernelYesSkipped
boot-completed event reported to kernelYesSet directly during init
post-fs-data.sh / post-fs-data.d/ scriptsYesReplaced by late-load stage
system.prop loadingYesYes
Magic Mount module mountingYesYes
post-mount.sh / post-mount.d/ scriptsYesYes
service.sh / service.d/ scriptsYesYes
boot-completed.sh / boot-completed.d/ scriptsYesYes
KSU_LATE_LOAD environment variableNot setSet to 1
Kernel info flag 0x4Not setSet

Script execution order ​

In late-load mode, the script execution order is:

txt
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:

sh
if [ "$KSU_LATE_LOAD" = "1" ]; then
    # Running in late-load mode
    echo "Late-load mode detected"
fi

This 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.

Released under the GPL3 License.