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:
benjamin committed 2026-08-19 22:33:42 +02:00
1 parent 4399a8288d
commit 72f4661bdc
72 files changed
+77927 -17

No files matched your search

+40
View File
@@ -0,0 +1,40 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: AGPL-3.0-or-later
# Verifier: reads the live Plex process to confirm the crack installed.
# arg1 = PID of "Plex Media Server"
# Checks:
# - apply_feature_list_xml (file vaddr 0x1167490) prologue overwritten with a
# trampoline JMP (FF 25 ...) => hook installed.
# - g_feature_bitset_slots (file vaddr 0x15AE5D8, 14 x u64) => feature bits.
import sys, struct, binascii
pid = int(sys.argv[1])
MAIN = "/usr/lib/plexmediaserver/Plex Media Server"
APPLY = 0x1167490
BITSET = 0x15AE5D8
base = None
with open("/proc/%d/maps" % pid) as f:
for line in f:
if line.rstrip().endswith(MAIN):
base = int(line.split("-", 1)[0], 16)
break
if base is None:
print("ERROR: base mapping not found")
sys.exit(1)
print("base = 0x%x" % base)
def rd(off, n):
with open("/proc/%d/mem" % pid, "rb") as f:
f.seek(base + off)
return f.read(n)
fn = rd(APPLY, 16)
print("apply_feature_list_xml[0:16] = " + binascii.hexlify(fn).decode())
print("hook installed (prologue == jmp FF 25)? %s" % (fn[:2] == b"\xff\x25"))
qs = struct.unpack("<14Q", rd(BITSET, 112))
for i, q in enumerate(qs):
print(" slot %2d = 0x%016x" % (i, q))
print("all 14 qwords fully 0xFF..F? %s" % all(q == 0xFFFFFFFFFFFFFFFF for q in qs))
print("all used low-bytes set (every feature enabled)? %s" % all((q & 0xFF) == 0xFF for q in qs))