# NeoZygisk: Zygisk API support for APatch and KernelSU through ptrace

> A C++ module that reimplements Magisk's Zygisk interface on top of ptrace, giving APatch and KernelSU the API their module ecosystem expects while keeping a tight grip on what an app can see.

**JingMatrix/NeoZygisk** — Zygote injection with ptrace

- Repository: https://github.com/JingMatrix/NeoZygisk
- Stars: 2,406 · Forks: 190
- Language: C++
- License: GPL-3.0
- Published: 2026-10-07 · Updated: 2026-10-07 · Language: en
- Canonical page: https://hysenlabs.com/projects/jingmatrix-neozygisk

## A second implementation of an interface rather than a fork of one

NeoZygisk describes itself as a Zygote injection module implemented via `ptrace` that provides Zygisk API support for APatch and KernelSU, and it also positions itself as a replacement for Magisk's built-in Zygisk. Both jobs come from the same root cause: the Zygisk API is an interface that a large body of already-written modules target, and if your root implementation does not provide it, that ecosystem does not run on your device.

The README's first core principle is API compatibility, and the way the project backs that claim is worth noting. Rather than only asserting it, the README points at the `loader/src/injector` folder and says the relevant API designs are mirrored there for reference. That is a real invitation to check the implementation against the one it claims to match, which is not something most projects in this space bother to do.

The second principle is a minimalist design, framed as focusing on a lean and efficient implementation and avoiding feature bloat for stability and performance. For a component that sits in the hot path of every app specialization on the device, that is the right instinct. The repository tree is consistent with a tight scope: `loader/`, `module/`, and `zygiskd/` are the working directories, alongside the Gradle files and the standard repo furniture. There is no sprawling source tree, and there are no `examples/` or sample modules.

## What the DenyList actually does to an app's view of the filesystem

The DenyList is the feature with the most explanatory value in the README, and the reason is that it reframes the problem correctly. Modern systemless root solutions work by creating overlay filesystems with `mount` rather than by writing to system partitions. So the thing that gives away a rooted device is not a binary, it is a modified view of the mount table.

NeoZygisk handles visibility with a two-row table. For an app granted root privileges, the mount namespace shows root solution mounts plus module mounts, because an app with root access is expected to see them and advanced file managers need to. For an app on the DenyList, the namespace is clean and unmodified, its root privileges may be revoked, and every trace of the root and module mounts is hidden. That last row is the whole point, and the README is candid that the app's privileges might be revoked as part of the trade.

The mechanics are two strategies rather than one. The primary is direct Zygote unmounting, described as experimental: NeoZygisk tries to unmount all root-related traces from the zygote process itself, which cleans the environment before an app process is fully specialized. The README frames that as a potentially more robust hiding mechanism, and it explains the safety check plainly, because a module providing critical system resources such as an overlay in `/product` will abort the direct unmount to prevent a zygote crash.

## The fallback is a cached clean namespace reached with setns

When the aggressive path is skipped for safety, or when traces fail to unmount, NeoZygisk reverts to what it calls its standard, reliable method. After the app process forks, the `setns` syscall switches it into a cached, completely clean mount namespace, isolating it from all system modifications.

Two design choices here are worth reading twice. The first is that the fallback is not a weaker version of the primary, it is the ordinary path, and the README calls the experimental one experimental. That framing inverts the usual relationship between a clever technique and its safe substitute, and it means a user who hits crashes on a specific device is encountering documented behaviour rather than a bug the project has not seen.

The second is caching. A clean namespace is built once and reused, rather than reconstructed per process. That matters because namespace transitions are not free, and every app launch on the device goes through this code. The release history shows this area getting attention: v2.3 migrated mount namespace transfers to Unix domain sockets with `SCM_RIGHTS`, which the release notes say resolved `Permission denied` errors on certain Android 12 arm32 devices when accessing namespace paths through `/proc`. Reusing a cached namespace is also why the older `/proc` path was a failure point at all.

## Configuration is split three ways depending on the root manager

Configuration lives in the root management app rather than in a file of NeoZygisk's own, and it differs by root solution. For APatch and KernelSU, you enable the `Umount modules` option for the target application. For Magisk, you use the `Configure DenyList` menu.

Then there is the warning, which is the most practically useful paragraph in the README. Magisk's `Enforce DenyList` option enables Magisk's own DenyList implementation, which is separate from NeoZygisk's functionality, is not guaranteed to hide all mount-related traces, and may conflict with NeoZygisk's hiding mechanisms. The README says it is strongly recommended to leave that option disabled and rely on NeoZygisk's configuration alone.

This is the kind of documentation that saves hours. Two overlapping hiding mechanisms that each partially work, layered on the same namespace, is exactly the configuration that produces a device that is neither cleanly rooted nor cleanly hidden. If you are setting this up, the two sentences about which toggle to leave off are the operative instruction; everything else in the README is background for understanding why it matters.

## Release notes read as a record of Android's moving internals

The three most recent releases are more informative about the project's difficulty than any feature list would be. The repository is not archived and the last push was on 2026-09-03, and v2.4 shipped on 2026-08-08, so this is an actively maintained line rather than a finished artifact.

v2.4 is largely a story of Android 17. It added the reworked app-specialize signatures for Android 17, specifically `useFifoUi` and `cgroupUid` on QPR2, plus GrapheneOS 17 for relocated `extraLongArgs`. The release notes explain the consequence directly: outdated signatures previously left app processes uninjected. They also added a fallback for the `ProtectedData` constructor and destructor symbols that are absent on the Android 17 preview, and changed behaviour so unresolved signatures are logged rather than skipped silently, which is a debugging improvement you only notice when something has gone wrong.

The device-level fixes in the same release are just as telling. ARMv9 BTI needed the stale `PSTATE.BTYPE` cleared before remote calls, fixing a SIGILL on `dlopen` via ptrace on a Pixel 10 Pro XL. Nested zygote startup, as seen in some VR headsets, added hierarchical tracing through stub processes for `init` to `stub_zygote` to `zygote` boot chains. The SELinux policy was updated to permit reading the mount namespace, fixing namespace updates under qemu emulators. Each of these is a device or kernel detail that no amount of reading the source would have predicted.

## Version floors, dropped support, and one honoured breaking change

v2.3, published on 2026-02-14, shows the project negotiating with KernelSU's own interface changes. It implemented the new `ioctl`-based supercall interface for KernelSU v20000 and up, replacing the deprecated `prctl` method. It also relaxed KernelSU version limits so community variants work, with warnings issued in logs instead of strict enforcement. That is a deliberate trade of strictness for reach, and the release notes state it rather than leaving you to discover it.

v2.2, from 2025-09-14, is the one release with a stated breaking change, and it is worth knowing because it narrows what the module can do. NeoZygisk no longer injects into 32-bit-only applications running on 64-bit devices, on the grounds that this resolves compatibility issues on Android 10 and that modern 32-bit-only apps are rare. The same release overhauled the logic for determining an app's mount namespace, fixing a bug where system apps installed as modules would fail to launch, and v2.1 was a hot fix for the Zygisk daemon failing to clean up LSPosed mounts.

Taken together, the release history suggests a project with a clear sense of what it will not do. It will not inject into 32-bit-only apps on 64-bit devices. It will not proceed with a direct unmount that risks a zygote crash over a module overlay in `/product`. It will log an unresolved signature rather than silently skipping injection. Those constraints are documented, and for a component this deep in the Android runtime, documented limits are worth more than a longer feature list.

## Conclusion

NeoZygisk is the piece of plumbing that lets the Zygisk module ecosystem work on root solutions that never shipped Magisk's own implementation. What it is genuinely good at is hiding the overlay filesystems that give away a systemless root, and it is unusually explicit about the two strategies it uses and the conditions under which it abandons the aggressive one. What it does not do is settle the question its name invites: the README states compatibility with Magisk's built-in Zygisk and points at a source folder for reference, but never walks through the API surface a module author should expect to find. Start by reading the injector sources if you are writing a module, and read the DenyList section twice if you are configuring it, because the interaction with Magisk's own Enforce DenyList is where people configure it wrongly.

## FAQ

### What are the differences between NeoZygisk and Zygisk Next?

Both provide the Zygisk API to root solutions other than Magisk, and they take different routes to doing it. NeoZygisk implements injection with `ptrace` and says it maintains full API compatibility with Magisk's built-in Zygisk, mirroring the relevant designs in its injector source. The README does not discuss Zygisk Next at all, so it gives you no basis for a feature-by-feature comparison and does not claim one or the other is the successor to it.

### Does NeoZygisk work as a replacement for Magisk's built-in Zygisk?

That is one of the two roles the README claims for it, the other being Zygisk API support for APatch and KernelSU. It states that the relevant API designs are mirrored in the `loader/src/injector` folder for reference, which is where you would check compatibility yourself. The README also cautions that Magisk's own Enforce DenyList is separate from NeoZygisk's DenyList and should be left disabled.

### How does NeoZygisk hide root from an app that checks for it?

Systemless root solutions mount overlay filesystems rather than writing to system partitions, so NeoZygisk attacks the mount namespace instead. Its primary strategy is to unmount root-related traces directly from the zygote process before an app is specialized, subject to a safety check that aborts if a module provides critical resources. When that is skipped, or leaves traces behind, it switches the forked app into a cached clean mount namespace with the `setns` syscall.

### Which Android versions and devices does NeoZygisk currently support?

The v2.4 release added the reworked app-specialize signatures for Android 17 and GrapheneOS 17, and fixed problems on ARMv9 BTI hardware, nested zygote boot chains on some VR headsets, and qemu emulators where SELinux policy blocked reading the mount namespace. One support was deliberately dropped in v2.2: 32-bit-only applications on 64-bit devices are no longer injected into.

## Sources

- [Issues](https://github.com/JingMatrix/NeoZygisk/issues)
- [JingMatrix/NeoZygisk on GitHub](https://github.com/JingMatrix/NeoZygisk)
- [License: GPL-3.0](https://github.com/JingMatrix/NeoZygisk/blob/master/LICENSE)
- [README](https://github.com/JingMatrix/NeoZygisk/blob/master/README.md)
- [Releases](https://github.com/JingMatrix/NeoZygisk/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/jingmatrix-neozygisk
