A Linux-native GUI for injecting .NET/C# mods into Unity & Mono games — and for setting up IL2CPP mod loaders — without touching a terminal or a launch script.
Made by TheHolyOneZ · Repo · Download · More projects · Game mods
Zyringe is the Linux-native equivalent of SharpMonoInjector: pick a running Unity/Mono game, pick a .dll, name an entry point (Namespace.Class.Method), and inject it — in-memory, no disk write, no launch wrapper. For IL2CPP games (which have no Mono runtime to inject into), it installs and manages MelonLoader or BepInEx for you, sets the Steam launch option automatically, and launches the game — so modding an IL2CPP title is a few clicks instead of a wiki crawl.
Warning
BETA. Mono injection (Attach & Launch) and IL2CPP loaders (MelonLoader & BepInEx) are all implemented and tested working end-to-end. It is marked BETA only because it hasn't been battle-hardened across every distro, game and edge case yet. Expect the occasional rough edge.
- Screenshots
- Who it's for
- Feature overview
- Target discovery
- Mono injection
- Entry-point browser & arguments
- IL2CPP mod loaders
- Interface & UX
- Security model
- Architecture
- Tech stack
- Platform support
- Requirements
- Build & run
- Packaging
- Usage walkthroughs
- Keyboard shortcuts
- Troubleshooting / FAQ
- Project layout
- Contributing
- License
- Disclaimer
|
Run existing C#/.NET mods in Unity games on Linux — the thing that used to require Windows or fragile scripts. |
Attach into a live game, load an assembly in memory, and call arbitrary static entry points with a real UI and a live log. |
IL2CPP games too — pick a loader, click install, click launch. No hand-editing Steam launch options. |
|
Mono injection (the tested core)
|
IL2CPP mod loaders (BETA)
|
|
Interface
|
Security
|
Zyringe scans /proc/*/maps for running Unity/Mono games — it never needs to have launched them.
| What it detects | How |
|---|---|
| Mono runtime + flavor | Matches libmonobdwgc-2.0 (BleedingEdge), libmonosgen-2.0 (SGen), libmono-2.0, Boehm — reported per target |
| IL2CPP games | Detects GameAssembly / libil2cpp — surfaced but routed to the loader flow, not injection |
| Windows-Mono under Proton | Detects mono-2.0-bdwgc.dll inside Wine → listed as can't attach (a Linux injector can't reach a Windows-DLL runtime) |
| Proton / Wine | From STEAM_COMPAT_DATA_PATH / WINEPREFIX / PROTON_VERSION in the process environment |
| Steam AppID | Read from /proc/<pid>/environ — enables the launch-option automation |
| Duplicate PIDs | A game shown under several PIDs (Steam runtime bootstrap/reaper) — the real one (most threads) is primary, the rest badged dup; nothing is hidden |
Each row shows engine, flavor, thread count and injectability; a right-click menu gives the full breakdown (name, PID, engine, runtime, threads, injectable, exe path, data dir, Mono .so, cmdline) plus quick actions. The list auto-refreshes on a configurable interval (1s / 2s / 5s / Off), and it's container-aware — it resolves symbols through /proc/<pid>/root so it works inside Steam's pressure-vessel.
Two modes share one self-contained C helper payload (libzyringe.so, links only libc, resolves every Mono symbol at runtime via dlsym).
Injects into an already-running game:
- A small privileged binary (
zyringe-inject) is elevated per-injection viapkexec(polkit). The GUI stays unprivileged. - It
ptrace-attaches, picks a safe syscall-parked thread (scored by state /wchan, avoiding the loader lock), and remote-dlopens the helper. - The helper does an SMI-grade in-memory load —
mono_image_open_from_data_with_name(the assembly is never written to disk, andAssembly.Locationis set to the real path), on a fresh attached worker thread, thenmono_runtime_invoke.
Details & hardening
- String arguments marshalled via
mono_string_new, passed to your entry method in order. - Return value captured via
mono_object_to_string; managed exceptions captured and reported. - Cooperative cancel — a cancel flag in your private
$XDG_RUNTIME_DIR(not world-writable/tmp; TOCTOU-safe) lets you abort a stuck injection without a second password prompt. - Configurable timeout on the invoke.
- 32-bit reject — reads the target's ELF class and refuses cleanly.
- noexec
/tmpfallback — parses the target'smountinfoand stages the helper into/tmp→/dev/shm→ the game dir as needed. - mmap-leak fix — the helper
munmaps its scratch page after a short grace so injection leaves no residue. - Container-safe symbol resolution via ELF
.dynsymparsing under/proc/<pid>/root.
Starts the game yourself with the helper LD_PRELOADed and the config passed by env — no elevation required. Crucially, Zyringe knows the injection is done the moment the helper reports success mid-startup (it watches the helper's markers and also polls the helper's own log file in case the game redirects stderr), so the UI resolves to Success while the game keeps running — instead of hanging until the game exits.
No more guessing method names. Browse methods parses the DLL's .NET metadata directly:
- Reads the PE → CLI header →
#~metadata tables (TypeDef/MethodDef), the#Stringsheap, and signature blobs. - Lists every candidate as
Namespace.Class.Method, grouped by type, searchable. - Badges each method static, void / non-void, and its argument count.
- A "Compatible only" filter shows just the valid entry points (static + void — arguments are fine, they're passed as strings).
- Picking a method auto-fills the entry-point fields and the right number of argument slots.
The Arguments editor lets you add/remove ordered string arguments passed to the entry method (each as a System.String).
IL2CPP compiles C# to native code — there is no Mono runtime to inject into. So instead of injecting, Zyringe provisions and manages an external loader the game runs at startup. Fully supported: MelonLoader and BepInEx (IL2CPP builds).
- Browse & install a build straight from the loader's GitHub releases (filtered to
.zipassets), or Install from a local zip. - Smart guards so you can't install the wrong thing:
- Proton games hide Linux and macOS builds (a Linux loader crashes a Wine game).
- BepInEx hides non-IL2CPP builds (a Mono/BepInEx-5 build silently no-ops on an IL2CPP game).
- Validated downloads — PK-magic check, zip-slip-safe extraction, exec bits preserved.
- Post-install verification — if the archive didn't actually produce a working loader (incomplete download, wrong asset, wrong variant, wrong platform), it fails loudly with a specific fix instead of a false "installed".
- Add / remove plugin DLLs and open the plugins folder in your file manager.
- Correct game-folder resolution even for Proton (where
/proc/<pid>/exeis Wine, not the game): usesSTEAM_COMPAT_INSTALL_PATH, then translates the Windows exe path from the game's argv (Z:\…→/…, other drives → the Wine prefix), then falls back to the process cwd — so it lands in the game folder for Steam, Proton, cracked/OnlineFix and non-Steam layouts alike. - Steam auto-setup — writes the correct launch option into Steam's own config (a targeted, backed-up edit of
localconfig.vdf; refuses while Steam is running so it can't be clobbered), then launches viasteam://run/<appid>. No pasting. - Non-Steam / Wine games get explicit, copyable guidance (the
WINEDLLOVERRIDESto set in Lutris / Bottles / Heroic or your own script). - A red banner warns if a Proton game has a loader installed but is missing its Windows proxy DLL (i.e. a Linux build slipped in).
|
|
|
|
React/TS frontend (Vite) ← unprivileged
│ Tauri v2 IPC (invoke + events)
src-tauri/ Rust backend ← unprivileged
├── scanner.rs find Unity/Mono games in /proc/*/maps
├── injector.rs orchestrate: pkexec helper / LD_PRELOAD launch
├── metadata.rs read a DLL's entry points (PE→CLI metadata)
├── loader.rs install & manage MelonLoader / BepInEx
├── steam.rs edit Steam launch options, launch via steam://
└── mono.rs locate the bundled privileged assets
│ pkexec (polkit)
▼
zyringe-inject (privileged Rust bin) ← elevated only for injection
├── ptrace_engine.rs ATTACH → remote dlopen(helper.so) → restore → DETACH
├── elf.rs / maps.rs resolve remote symbols via /proc + ELF .dynsym
└── main.rs arg parsing, staging, cancel/timeout
│ dlopen
▼
helper/libzyringe.so (C, libc-only) ← runs inside the game
└── resolve Mono syms → attach thread → assembly_open → runtime_invoke
| Layer | Technology |
|---|---|
| Frontend | React 18 · TypeScript · Vite · lucide-react |
| App backend | Rust · Tauri v2 (webkit2gtk) |
| Privileged injector | Standalone Rust binary — ptrace / process_vm_writev, elevated via pkexec + polkit |
| In-target payload | C shared object — links only libc, resolves Mono symbols at runtime via dlsym |
| Loader downloads | curl + the zip crate (no bundled TLS stack) |
| Metadata parsing | Hand-rolled ECMA-335 (PE → CLI → #~ tables) reader in Rust |
| Target | Injection | Modding | Notes |
|---|---|---|---|
| Native-Linux Mono (Unity) | ✅ Attach + Launch | ✅ | The tested core path |
| Native-Linux IL2CPP | — | ✅ MelonLoader / BepInEx | Loader-based |
| Proton/Windows IL2CPP | — | ✅ Windows loader + Steam auto-setup | Uses the Windows loader build |
| Proton/Windows Mono | ❌ can't attach | — | Its Mono runtime is a Windows DLL inside Wine; clearly flagged |
| Architecture | x86_64 only | ||
Verified pipelines: Mono Attach · Mono Launch · IL2CPP MelonLoader · IL2CPP BepInEx. Tested on: EndeavourOS · Linux 6.18 LTS · x86_64 · KDE / X11 · glibc 2.43.
- x86_64 Linux with
pkexec(polkit),curl, and a desktop portal (xdg-open). - Build toolchain: Node + pnpm, Rust (stable), a C compiler, and the Tauri v2 system deps (
webkit2gtk-4.1,gtk3,libsoup-3.0,openssl).
# 0. one-time: generate the app icons from your logo
pnpm tauri icon path/to/logo.png
# 1. build everything and launch (icons → helper .so → zyringe-inject → deps → app)
bash scripts/dev.sh
# or build only, don't launch:
bash scripts/dev.sh --build
# 2. install the privileged bits ONCE (needed for Attach/Launch)
bash scripts/build-resources.sh # build helper + zyringe-inject (as you)
sudo bash scripts/install.sh # install to /usr/lib/zyringe + polkit rule (as root)install.sh puts zyringe-inject + libzyringe.so under /usr/lib/zyringe (root-owned) and a polkit rule that pins that exact path. Remove with:
sudo rm -rf /usr/lib/zyringe /etc/polkit-1/rules.d/49-zyringe.rulesbash scripts/package.shBuilds the helper + privileged injector, runs tauri build, and collects everything into ./dist/:
dist/
├── Zyringe_<ver>_amd64.AppImage portable GUI
├── Zyringe_<ver>_amd64.deb GUI as a Debian/Ubuntu package
├── zyringe-inject privileged ptrace binary
├── libzyringe.so in-target helper payload
├── install.sh 49-zyringe.rules privileged-bits installer + polkit rule
└── INSTALL.txt post-install steps
The GUI bundle is unprivileged; run sudo bash install.sh from ./dist/ once to install the polkit-pinned injection bits.
Inject a mod into a Mono game (Attach)
- Launch your Unity/Mono game; it appears in the left Targets list.
- Click it (Mono games are injectable).
- Choose your Mod DLL and its entry point — use Browse methods to pick a
static voidmethod straight from the assembly. - (Optional) add string Arguments.
- Click Inject → approve the
pkexecprompt → watch the console. On success your entry point has run; the game keeps playing.
Inject at launch (LD_PRELOAD)
- Switch the mode toggle to Launch.
- Set the Game executable path.
- Choose your DLL + entry point (+ args).
- Click Launch with Mod — Zyringe starts the game with the helper preloaded and reports success the moment your entry point runs.
Mod an IL2CPP game (MelonLoader / BepInEx)
- Launch the IL2CPP game; select it — it opens the loader panel (not the inject form).
- Pick a loader, then Browse & install a build (Zyringe only shows builds that fit your game/platform) or Install from zip.
- Add plugin → your mod
.dll. - For a Steam game: close Steam, then Set up & launch via Steam — Zyringe writes the launch option and starts the game. For non-Steam/Wine, copy the shown
WINEDLLOVERRIDESinto your launcher. - Verify from the loader's own log (e.g.
BepInEx/LogOutput.log).
| Ctrl + Enter | Inject / Launch |
| Esc | Cancel an in-flight injection |
| Ctrl + L | Clear the console |
The Inject button is greyed out.
The hint next to it tells you the first missing requirement (no target selected, no DLL, missing class/method, etc.). Fill it in.My IL2CPP mod didn't load.
Almost always the wrong loader build. For a Proton game you need a Windows IL2CPP build (not Linux, not the Mono/BepInEx-5 build). Zyringe now hides the wrong ones and rejects a mismatched install — reinstall with a build whose name containsIL2CPP and win-x64.
"Steam is running" when setting the launch option.
Steam rewrites its config on exit, so Zyringe refuses to edit it while Steam is open. Fully quit Steam (Steam → Exit), then try again.A Windows game under Proton shows "can't attach".
Its Mono runtime is a Windows DLL living inside Wine — a Linux injector can't reach it. Attach/Launch work on native-Linux Mono games; use the loader flow for Proton/Windows games.The game crashes on launch even without any mod.
That's the game, not Zyringe — check itsPlayer.log. (Common culprit on old builds: expired TLS certs breaking the game's own telemetry.)
src/ React + TypeScript frontend
components/ UI (process list, inject form, loader panel, …)
src-tauri/src/ Rust app backend (scanner, injector, loader, steam, metadata, mono)
zyringe-inject/ Standalone privileged ptrace binary
helper/ C in-target payload (libzyringe.so) + Makefile
scripts/ dev / build / install / package
packaging/ polkit rule
Issues and PRs welcome. This is BETA — bug reports with the game, distro, loader/build, and the relevant Player.log / loader log are especially useful. By contributing you agree your contributions are licensed under GPL-3.0-only.
| 📦 Source (GitHub) | github.com/TheHolyOneZ/Zyringe |
| ⬇️ Download (pre-compiled) | zlogic.eu/zyringe — the landing & download page for ready-to-run builds (GitHub is source only) |
| 👤 Author | @TheHolyOneZ |
| 🧰 More projects | zsync.eu |
| 🎮 Game mod menus | zlogic.eu/mods |
GPL-3.0-only © 2026 TheHolyOneZ. See LICENSE.
Zyringe uses ptrace / process_vm_writev to load code into other processes — the same capability as any debugger or SharpMonoInjector. It requires admin authorization per injection and is intended for modding games you own and for development. x86_64 Linux, Mono/Unity targets only.

