ZygiskFrida Documentation

repository·main·Indexed 21 days ago

https://github.com/lico-n/zygiskfrida

A Zygisk (or Riru) module that injects a Frida gadget into Android applications. It provides a stealthy alternative to frida-server by avoiding ptrace-based detection and maintaining APK integrity. Features include advanced JSON configuration for target applications, startup delay to bypass anti-Frida checks, custom library injection, and experimental child gating to instrument forked processes.

Tokens
4.2K
Snippets
15
Records
18
Agent score
77%

What's inside ZygiskFrida

  1. Use Child Gating to instrument forked processes

    main

    Child gating is an experimental feature that allows ZygiskFrida to intercept fork/vfork calls to instrument child processes. This is useful when an application runs security checks in a separate process.

    Modes of Operation:

    • freeze: The child process is prevented from returning from the fork. It stays alive but executes no code.
    • kill: The child process is killed immediately upon forking.
    • inject: The injected_libraries are loaded into the child process, and then it resumes normal execution.

    Important Caveats for inject mode:

    1. Library Duplication: You cannot load the same gadget file into the child process that is already in the parent (even via symbolic link). You must use a physical copy of the gadget.
    2. Port Conflicts: The child gadget will conflict with the parent gadget's port. You must provide a separate configuration file for the child gadget (e.g., /data/local/tmp/re.zyg.fri/libgadget-child.config.so) with a different port.
    3. Cleanup: Using child gating may prevent apps from shutting down normally. If an app becomes unresponsive, manually kill it using: adb shell 'su -c kill -9 $(pidof <package_name>)'
    # Step 1: Create a physical copy of the gadget for the child
    adb shell 'su -c cp /data/local/tmp/re.zyg.fri/libgadget.so /data/local/tmp/re.zyg.fri/libgadget-child.so'
    
    # Step 2: Configure the child gadget with a unique port in /data/local/tmp/re.zyg.fri/libgadget-child.config.so
    # (See 'Configure child gadget port' for JSON structure)
    
    # Step 3: Connect to the child gadget
    adb forward tcp:27043 tcp:27043
    frida -H 127.0.0.1:27043 -n Gadget
  2. Load arbitrary libraries into the process

    main

    You can load additional .so libraries into the target application process by creating a file at /data/local/tmp/re.zyg.fri/injected_libraries.

    • Each line in the file should be a full path to a library.
    • Libraries are loaded in the order they appear in the file.
    • Ensure the libraries are in a location accessible by the app and have correct permissions.

    Important: If you want the Frida gadget to run, you must explicitly include the path to the gadget (either the bundled one or your custom one) in this file. If you omit the gadget path from this file, the gadget will not start.

    /data/local/tmp/re.zyg.fri/libhelperexample.so
    /data/local/tmp/re.zyg.fri/libgadget.so
  3. Install ZygiskFrida via Magisk

    main

    ZygiskFrida is a Zygisk module that injects a Frida gadget into Android applications stealthily without modifying the APK or using ptrace.

    To install:

    1. Download the latest release from the GitHub Releases page.
      • Use the normal version for Zygisk.
      • Use the riru-release if you are using Riru instead of Zygisk.
    2. Transfer the .zip file to your device.
    3. Install the zip file via the Magisk app.
    4. Reboot your device.
    # Download from: https://github.com/lico-n/ZygiskFrida/releases
    # Install via Magisk app on your device
  4. Add a startup delay to package injection

    main

    To avoid detection by applications that perform startup checks, you can delay the Frida gadget injection. In the /data/local/tmp/re.zyg.fri/target_packages file, append a comma and the delay duration in milliseconds to the package name.

    Example: com.example.package,20000 will delay injection by 20 seconds.

    You can monitor the countdown to injection (which lasts 10 seconds) by checking the ZygiskFrida logs using: adb logcat -S ZygiskFrida.

    adb shell 'su -c "echo com.example.package,20000 > /data/local/tmp/re.zyg.fri/target_packages"'
  5. Configure target packages for injection

    main

    To specify which Android applications ZygiskFrida should inject into, create a text file at /data/local/tmp/re.zyg.fri/target_packages. This file should contain one package name per line.

    Note: This is the legacy configuration method. For full feature support, use the structured config instead.

    adb shell 'su -c "echo com.example.package > /data/local/tmp/re.zyg.fri/target_packages"'
  6. Configure ZygiskFrida for a target application

    main

    After installation and reboot, you must create a configuration file to specify which application should receive the Frida gadget injection. The configuration file is located at /data/local/tmp/re.zyg.fri/config.json.

    Use the following commands to initialize the config from the example and set your target package name:

    1. Copy the example config to the active config file.
    2. Use sed to replace the placeholder package name with your target application's package name.
    adb shell 'su -c cp /data/local/tmp/re.zyg.fri/config.json.example /data/local/tmp/re.zyg.fri/config.json'
    adb shell 'su -c sed -i s/com.example.package/your.target.application/ /data/local/tmp/re.zyg.fri/config.json'
  7. Configure ZygiskFrida using the Advanced JSON method

    main

    The preferred way to configure ZygiskFrida is via a structured JSON configuration file located at /data/local/tmp/re.zyg.fri/config.json. This method supports advanced features like child process gating and custom library injection.

    If you are using both the simple configuration method and this advanced method, the advanced configuration takes precedence for any overlapping applications.

    To initialize your configuration, copy the provided example file to the active config path using ADB:

    adb shell 'su -c cp /data/local/tmp/re.zyg.fri/config.json.example /data/local/tmp/re.zyg.fri/config.json'
  8. Build ZygiskFrida from source

    main

    To build the Magisk module manually:

    1. Checkout the repository.
    2. Run the Gradle assemble task.
    3. The resulting Magisk module will be located in the out directory.

    Alternatively, you can build and flash the module to your device and reboot in a single step.

    # Build the module
    ./gradlew :module:assembleRelease
    
    # Build, flash to device, and reboot
    ./gradlew :module:flashAndRebootZygiskRelease
  9. How Zygisk companion processes work

    main

    If your module needs to perform operations with superuser (root) permissions or share resources across multiple processes, you should use a Companion Process.

    1. Register a handler: Use REGISTER_ZYGISK_COMPANION(func) to define a function that runs in a root-privileged daemon process. This function receives an integer representing a Unix domain socket.
    2. Connect from the target process: In your ModuleBase (specifically during preAppSpecialize due to SELinux restrictions), call zygisk::Api::connectCompanion() to obtain a file descriptor for the socket.
    3. IPC: Use the returned file descriptor to perform Inter-Process Communication (IPC) with the companion process.

    The companion process is ABI-aware: a 32-bit target process connects to a 32-bit companion, and a 64-bit target connects to a 64-bit companion.

    // In your module file
    static void my_companion_handler(int socket) {
        // Handle IPC requests from target processes
    }
    
    REGISTER_ZYGISK_COMPANION(my_companion_handler)
    
    // Inside your ModuleBase::preAppSpecialize
    void preAppSpecialize(zygisk::AppSpecializeArgs *args) override {
        int fd = api->connectCompanion();
        if (fd != -1) {
            // Use fd for IPC
        }
    }
  10. Configure target application injection settings

    main

    Within the targets array in config.json, you define how Frida interacts with specific applications. Each target object supports the following keys:

    • app_name: The bundle ID of the application to inject.
    • enabled: A boolean to toggle the configuration for this target without deleting it.
    • start_up_delay_ms: An integer representing the delay in milliseconds before library injection occurs. Use this to bypass startup-time anti-Frida checks.
    • injected_libraries: An array of objects specifying the libraries to load. Libraries are loaded in the order they appear in the array.

    Note on Libraries:

    • The module provides default gadgets at /data/local/tmp/re.zyg.fri/libgadget.so (device architecture) and /data/local/tmp/re.zyg.fri/libgadget32.so (for 32-bit apps on 64-bit devices).
    • You can replace these with paths to your own Frida gadgets or arbitrary libraries. Ensure the files have correct permissions and are accessible by the app. If you encounter permission issues, place your libraries inside the re.zyg.fri directory and reinstall the module.
    {
        "targets": [
            {
                "app_name" : "com.example.package",
                "enabled": true,
                "start_up_delay_ms": 0,
                "injected_libraries": [
                    {
                        "path": "/data/local/tmp/re.zyg.fri/libgadget.so"
                    }
                ]
            }
        ]
    }
  11. Configure child gadget port and conflict behavior

    main

    When using inject mode for child processes, you must prevent port conflicts by creating a specific configuration file for the child gadget. Place this file at /data/local/tmp/re.zyg.fri/libgadget-child.config.so.

    To handle multiple children, use "on_port_conflict": "pick-next" to ensure each child gadget attempts to bind to the next available port.

    {
      "interaction": {
        "type": "listen",
        "address": "127.0.0.1",
        "port": 27043,
        "on_port_conflict": "pick-next",
        "on_load": "wait"
      }
    }