Replace patchelf crack with Freeloader LD_PRELOAD approach
- Multi-stage Dockerfile: discover patterns from PMS binary (capstone), compile .so with zig (musl), layer onto lscr.io/linuxserver/plex - Uses LD_PRELOAD instead of patchelf (which corrupts Plex's musl loader) - Auto-discovery: broad structural patterns with string-anchored fallback (//feature) and relationship-based fallback (BITSET_REF within BS_INIT) - hook.cpp uses __has_include for generated patterns with hardcoded fallbacks - Custom wrapper.sh (no traffic_logger preload) - Vendored Freeloader source (github.com/authrequest/Freeloader, AGPL-3.0) - Removed stale plexmediaserver_crack.so binary - Supports Plex 1.43.3+ (verified against 1.43.2 and 1.43.3)
This commit is contained in:
1 parent
4399a8288d
commit
72f4661bdc
72 files changed
+77927
-17
No files matched your search
@@ -0,0 +1,145 @@
|
||||
# Plex_Patch
|
||||
|
||||
Reverse-engineering notes and tooling for **Plex Media Server** on **Linux
|
||||
x86-64** — covering both *feature unlocking* and *remote access*.
|
||||
|
||||
> ⚠️ **Disclaimer** — For educational and reverse-engineering purposes, on
|
||||
> software you legally run yourself. Nothing here bypasses account or server
|
||||
> authentication, and **no Plex code** is included or redistributed. If you rely
|
||||
> on Plex, buy a Plex Pass — it funds the developers. Use at your own risk; no
|
||||
> warranty.
|
||||
|
||||
## What's here
|
||||
|
||||
| # | Component | Path | Summary |
|
||||
|---|-----------|------|---------|
|
||||
| 1 | **Feature-unlock patch** | `src/`, `build.sh` | `LD_PRELOAD` shared library: forces every `FeatureManager` bit on **and** adds webhook CRUD via socket interception |
|
||||
| 2 | **Relay RE + model** | `plex_relay/` | Reverse-engineered, runnable reimplementation of Plex's `RelayController` |
|
||||
| 3 | **Remote access (no patch)** | `scripts/plex-tailnet/` | Reach your server over Tailscale/Headscale instead of Plex Relay |
|
||||
| 4 | **Docker support** | `docker/`, [`docs/DOCKER.md`](docs/DOCKER.md) | Patched `plexinc/pms-docker` / `lscr.io/linuxserver/plex` images (multi-stage build) **and** in-place patcher for a running container (`plex-docker-patch.sh`) |
|
||||
|
||||
Each subsystem has its own README; this page is the map.
|
||||
|
||||
---
|
||||
|
||||
## 1 · Feature-unlock patch
|
||||
|
||||
Plex's feature gates read a single in-memory table, `g_feature_bitset_slots`
|
||||
(14 × `uint64`), populated from the MyPlex feature list. A feature with internal
|
||||
code `C` is "available" iff `slots[C >> 3] & (1 << (C & 7))`. The patch (`src/`)
|
||||
is a small shared library whose constructor finds
|
||||
`FeatureManager_apply_feature_list_xml`, installs a trampoline, and forces all 14
|
||||
slots to `0xFF…FF` after Plex applies its feature list — so every feature
|
||||
(including Plex Pass, code 92) reads as enabled.
|
||||
|
||||
Two non-obvious requirements make or break this on a real install:
|
||||
|
||||
1. **Build against musl, not glibc.** Plex bundles its own musl libc + libgcompat
|
||||
(`/usr/lib/plexmediaserver/lib/`). A glibc-built `.so` fails to relocate
|
||||
glibc-only symbols and Plex exits 127. The build uses `zig` to target
|
||||
`x86_64-linux-musl`.
|
||||
2. **Inject with `LD_PRELOAD`, never `patchelf`.** `patchelf --add-needed`
|
||||
corrupts the PIE under musl's loader (instant SIGSEGV). A tiny launcher sets
|
||||
`LD_PRELOAD` only for the Plex `exec`, and the library `unsetenv`s it so
|
||||
Plex's glibc helper children are unaffected.
|
||||
|
||||
```bash
|
||||
bash build.sh # -> build/plexmediaserver_crack.so (musl); prints install steps
|
||||
```
|
||||
|
||||
**Webhook socket interceptor.** The same `.so` also hooks POSIX socket functions
|
||||
(`read`, `recvfrom`, `sendmsg`) to intercept `/api/v2/user/webhooks` HTTP requests
|
||||
made by the Plex Web client to the local server. Instead of returning PMS's native
|
||||
404 (the endpoint only exists on plex.tv, not locally), the hook serves a complete
|
||||
webhook CRUD API backed by a JSON file at `/var/lib/plexmediaserver/webhooks.json`:
|
||||
|
||||
- `GET /api/v2/user/webhooks` — list all webhooks
|
||||
- `POST /api/v2/user/webhooks` — add webhook(s) from `urls[]=` form body
|
||||
- `PUT /api/v2/user/webhooks/:id` — update a webhook
|
||||
- `DELETE /api/v2/user/webhooks/:id` — delete a webhook
|
||||
- `OPTIONS` — CORS preflight
|
||||
|
||||
After every mutating operation, the hook calls into Plex's in-process
|
||||
`WebhookManager` to refresh the dispatch vector, so changes take effect without
|
||||
a server restart. The webhook file path can be overridden with the
|
||||
`PLEX_WEBHOOKS_FILE` environment variable. The Plex Web bundle also needs a
|
||||
one-time static patch so its JavaScript talks to `window.location.origin`
|
||||
instead of the Plex cloud API — see [`AGENTS.md`](AGENTS.md) for details.
|
||||
|
||||
Full build / install / uninstall guide: **[`docs/BUILD.md`](docs/BUILD.md)**.
|
||||
|
||||
## 2 · Plex Relay — `plex_relay/`
|
||||
|
||||
A study of how Plex makes a server reachable when no direct connection exists: it
|
||||
opens a **reverse SSH tunnel to a Plex-operated relay host**. `plex_relay/` is a
|
||||
clean-room, dependency-free Python reimplementation of the `RelayController`
|
||||
translation unit (key fetch + 24h cache, `relayHostKey.txt` pinning, the ssh
|
||||
tunnel, the 300s reaper), with a typed error model, injected I/O seams, and a
|
||||
full test suite. See **[`plex_relay/README.md`](plex_relay/README.md)**.
|
||||
|
||||
## 3 · Remote access without patching — `scripts/plex-tailnet/`
|
||||
|
||||
The pragmatic alternative to both Plex Relay and patching: put the server and its
|
||||
viewers on a **Tailscale/Headscale mesh VPN** and let Plex publish the tailnet
|
||||
address. Includes an idempotent setup script (security questionnaire, firewall
|
||||
lockdown, health check), an optional self-hosted Headscale installer, and a
|
||||
shared shell library. See **[`scripts/plex-tailnet/README.md`](scripts/plex-tailnet/README.md)**.
|
||||
|
||||
## 4 · Docker support — `docker/`
|
||||
|
||||
Same `LD_PRELOAD`-on-the-PMS-exec patch, packaged for the two popular Plex
|
||||
container images. Two flows are supported:
|
||||
|
||||
- **Rebuild a patched image** — multi-stage Dockerfiles (`Dockerfile.plexinc`,
|
||||
`Dockerfile.linuxserver`) build the musl `.so` with `zig`, layer it onto
|
||||
the upstream image, and replace the s6 `svc-plex` `run` file. Best for
|
||||
repeat deploys and CI/CD.
|
||||
- **Patch a running container in place** — `plex-docker-patch.sh` modifies
|
||||
the live container's filesystem (`.so`, wrapper, s6 `run` file) and
|
||||
restarts it. No image rebuild, original image untouched, fully
|
||||
revertible. Best for one-off patching of a container you don't want
|
||||
to touch.
|
||||
|
||||
The wrapper sets `LD_PRELOAD` *last* and the `.so`'s constructor `unsetenv`s
|
||||
it, so glibc helper children (Tuner, Script Host, transcoders) are unaffected.
|
||||
See **[`docker/README.md`](docker/README.md)** and the full guide
|
||||
**[`docs/DOCKER.md`](docs/DOCKER.md)**.
|
||||
|
||||
---
|
||||
|
||||
## Repository layout
|
||||
|
||||
| Path | What |
|
||||
|------|------|
|
||||
| `src/hook.cpp` · `hook.hpp` | hooking engine: `dl_iterate_phdr` discovery, signature scan, trampoline, feature logic, feature-UUID catalog |
|
||||
| `src/main.cpp` | library constructor (`unsetenv` + `hook()`) |
|
||||
| `src/webhook_handler.cpp` · `webhook_handler.hpp` | socket-level HTTP interceptor: hooks `read`/`recvfrom`/`sendmsg` to intercept `/api/v2/user/webhooks` and serve local CRUD from a JSON file |
|
||||
| `build.sh` | musl build via `zig` (auto-downloaded) with an ABI sanity gate |
|
||||
| `scripts/plex-crack-wrapper.sh` | systemd `ExecStart` launcher scoping `LD_PRELOAD` to the Plex process |
|
||||
| `scripts/readbitset.py` | verifier: dumps the live feature bitset from a running PMS |
|
||||
| `scripts/plex-tailnet/` | Tailscale/Headscale remote-access setup (see its README) |
|
||||
| `plex_relay/` | Python reimplementation of Plex's `RelayController` (see its README) |
|
||||
| `windows/` | Windows x64 DLL injector + godmode patch (see its README) |
|
||||
| `docker/` | patched `plexinc/pms-docker` + `lscr.io/linuxserver/plex` images + in-place patcher for running containers (see its README) |
|
||||
| `third_party/zydis/` | vendored [Zydis](https://github.com/zyantific/zydis) disassembler (MIT) |
|
||||
| `docs/BUILD.md` | native Linux build / install / uninstall guide |
|
||||
| `docs/DOCKER.md` | Docker build / run / verify / uninstall / troubleshooting guide |
|
||||
| `docs/WINDOWS.md` | top-level Windows x64 patching/build index |
|
||||
| `experimental/debug_hook.c` | standalone alternate hook (legacy signature) |
|
||||
| `AGENTS.md` | architecture / RE notes |
|
||||
|
||||
## Not in this repo (by design)
|
||||
|
||||
The copyrighted Plex binaries (`Plex Media Server`, `libsoci_core.so`), the IDA
|
||||
Pro databases (`*.i64`, `*.id0`, …), the auto-downloaded `toolchain/`, and any
|
||||
local machine config (`.mcp.json`, keys, `.env`) are intentionally
|
||||
**git-ignored** — they are large, sensitive, or not ours to distribute. Point
|
||||
your own analysis tools at your own Plex install.
|
||||
|
||||
## License
|
||||
|
||||
[GNU AGPL-3.0-or-later](LICENSE) © the Plex_Patch authors. Each source file
|
||||
carries an `SPDX-License-Identifier: AGPL-3.0-or-later` tag.
|
||||
|
||||
The vendored Zydis disassembler in `third_party/zydis/` is **MIT**-licensed (see
|
||||
`third_party/zydis/README.md`); its terms are preserved and unaffected.
|
||||
Reference in new issue
Block a user