Skip to content

Add standalone mode to trace command for late zygote injection#107

Open
JingMatrix wants to merge 5 commits intomasterfrom
init
Open

Add standalone mode to trace command for late zygote injection#107
JingMatrix wants to merge 5 commits intomasterfrom
init

Conversation

@JingMatrix
Copy link
Copy Markdown
Owner

This commit introduces the --standalone flag to the zygisk-ptrace64 binary, enabling the initialization of NeoZygisk after the zygote process has already started. This provides an alternative to relying strictly on Magisk's init phase injection.

To support injecting into an active, running process, several structural adjustments were made to the injection flow:

CLI and Daemon Initialization:

  • The trace command now parses the --standalone flag, which is mutually exclusive with --restart.
  • In standalone mode, the tracer resolves and changes the working directory to the module directory, then initializes the daemon controller prior to injection. This ensures the UNIX domain sockets are listening before the injected library attempts to connect.
  • Added SIGINT and SIGTERM handlers to safely detach and send SIGCONT to zygote if the tracer is aborted by the user, preventing system lockups.

Ptrace Flow Adjustments:

  • Omitted PTRACE_O_EXITKILL in standalone mode to ensure the zygote process is not terminated if the tracer exits unexpectedly.
  • Updated the PTRACE_SEIZE strategy to explicitly issue PTRACE_INTERRUPT. Unlike standard Magisk mode where zygote is already suspended via SIGSTOP, a running process must be manually interrupted.
  • Handled the resulting SIGTRAP stop and bypassed the SIGCONT wake-up sequence during detach, as a clean detach is sufficient to resume a process stopped via PTRACE_INTERRUPT.

Injection Logic:

  • Extracted the remote code execution sequence (dlopen, dlsym, entry) into a shared function to maintain DRY principles.
  • Created inject_standalone to bypass the AT_ENTRY stack parsing and memory hijacking. Since the dynamic linker is already initialized, the payload is invoked immediately.
  • Implemented a cross-architecture syscall restart mitigation. Interrupting a tracee inside a blocking system call (e.g., epoll_wait) and altering the instruction pointer triggers the kernel's ERESTARTSYS rewind logic, causing the instruction pointer to jump into invalid padding and throw a SIGTRAP. This is resolved by clearing orig_rax (and architecture equivalents) in the temporary registers before the remote call, while preserving the actual system call state in the backup registers for a seamless resumption.

This commit introduces the --standalone flag to the zygisk-ptrace64 binary, enabling the initialization of NeoZygisk after the zygote process has already started. This provides an alternative to relying strictly on Magisk's init phase injection.

To support injecting into an active, running process, several structural adjustments were made to the injection flow:

CLI and Daemon Initialization:
- The trace command now parses the --standalone flag, which is mutually exclusive with --restart.
- In standalone mode, the tracer resolves and changes the working directory to the module directory, then initializes the daemon controller prior to injection. This ensures the UNIX domain sockets are listening before the injected library attempts to connect.
- Added SIGINT and SIGTERM handlers to safely detach and send SIGCONT to zygote if the tracer is aborted by the user, preventing system lockups.

Ptrace Flow Adjustments:
- Omitted PTRACE_O_EXITKILL in standalone mode to ensure the zygote process is not terminated if the tracer exits unexpectedly.
- Updated the PTRACE_SEIZE strategy to explicitly issue PTRACE_INTERRUPT. Unlike standard Magisk mode where zygote is already suspended via SIGSTOP, a running process must be manually interrupted.
- Handled the resulting SIGTRAP stop and bypassed the SIGCONT wake-up sequence during detach, as a clean detach is sufficient to resume a process stopped via PTRACE_INTERRUPT.

Injection Logic:
- Extracted the remote code execution sequence (dlopen, dlsym, entry) into a shared function to maintain DRY principles.
- Created inject_standalone to bypass the AT_ENTRY stack parsing and memory hijacking. Since the dynamic linker is already initialized, the payload is invoked immediately.
- Implemented a cross-architecture syscall restart mitigation. Interrupting a tracee inside a blocking system call (e.g., epoll_wait) and altering the instruction pointer triggers the kernel's ERESTARTSYS rewind logic, causing the instruction pointer to jump into invalid padding and throw a SIGTRAP. This is resolved by clearing orig_rax (and architecture equivalents) in the temporary registers before the remote call, while preserving the actual system call state in the backup registers for a seamless resumption.
JingMatrix added a commit to JingMatrix/Vector that referenced this pull request Mar 10, 2026
In JingMatrix/NeoZygisk#107, NeoZygisk is modified to support Zygisk initialization without relying on the early init phase hooks of Magisk. This commit adds support for Vector to operate under this late injection model.

Modifications include:
- Daemon: Added parsing for the --late-inject flag in ServiceManager. When active, the daemon uses "serial_vector" as the proxy service name and LSPosedService manually dispatches the boot completed event.
- IPC Bridge: Updated RequestSystemServerBinder to accept a dynamic rendezvous service name instead of hardcoding it.
- Native Module: VectorModule now reads RuntimeFlags::LATE_INJECT during server specialization, adjusts the bridge service name accordingly, and passes the state to the Java payload.
- Framework: Updated Main.forkCommon to manually bootstrap the system_server environment during late injection. This extracts the ClassLoader from the live activity service, deoptimizes system server methods, and synchronously fires Xposed and LibXposed load callbacks.
Complicated modules, such as LSPosed / Vector, need to be inform about the injection mode, since many of their functions could reply on the exact launching sequence of system services.
@huynhbaman
Copy link
Copy Markdown

All good, but still detect ptrace, Do u have plan to hide its , bro

@JingMatrix
Copy link
Copy Markdown
Owner Author

@huynhbaman ptrace is not detectable as you may imagine, and the purpose of this pull-request is not about hiding. This pull-request adds support for AOSP debug builds without installing any root solution, an advanced scenario that is out of the scope of most users.

Current hiding mechinism is already good. Please avoid commenting about something you don't truly understand. If you want to ask questions, open a thread in Discussions.

JingMatrix added a commit to JingMatrix/Vector that referenced this pull request Mar 11, 2026
In JingMatrix/NeoZygisk#107, NeoZygisk is modified to support Zygisk initialization without relying on the early init phase hooks of Magisk. This commit adds support for Vector to operate under this late injection model.

Modifications include:
- Daemon: Added parsing for the --late-inject flag in ServiceManager. When active, the daemon uses "serial_vector" as the proxy service name and LSPosedService manually dispatches the boot completed event.
- IPC Bridge: Updated RequestSystemServerBinder to accept a dynamic rendezvous service name instead of hardcoding it.
- Native Module: VectorModule now reads RuntimeFlags::LATE_INJECT during server specialization, adjusts the bridge service name accordingly, and passes the state to the Java payload.
- Framework: Updated Main.forkCommon to manually bootstrap the system_server environment during late injection. This extracts the ClassLoader from the live activity service, deoptimizes system server methods, and synchronously fires Xposed and LibXposed load callbacks.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants