mirror of
https://gitgud.io/yuv420p10le/plexmediaserver_crack
synced 2025-07-04 16:34:47 +00:00
Compare commits
19 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1906022184 | ||
![]() |
a3001d1550 | ||
![]() |
1209483a5b | ||
![]() |
36046155d1 | ||
![]() |
7d5a79c019 | ||
![]() |
d4f1d18bac | ||
![]() |
c5ef9d2c63 | ||
![]() |
d9ec635fbd | ||
![]() |
8f89b436c1 | ||
![]() |
a1d4357d74 | ||
![]() |
0d3f011bba | ||
![]() |
e4fbde6a83 | ||
![]() |
3e706caf78 | ||
![]() |
edfc96ae60 | ||
![]() |
87a6614a6c | ||
![]() |
f1efa3884f | ||
![]() |
bb9a6dd9c3 | ||
![]() |
c89c3cc7bf | ||
![]() |
735bec04bd |
2
.gitignore
vendored
2
.gitignore
vendored
@ -213,9 +213,7 @@ FodyWeavers.xsd
|
||||
*.obj
|
||||
*.gch
|
||||
*.pch
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
*.mod
|
||||
*.smod
|
||||
*.lai
|
||||
|
78
README.md
78
README.md
@ -8,16 +8,19 @@ Unlocking *most features* on (hardware transcoding, intro/credit detection, HEVC
|
||||
|
||||
Note: x86-64 only. If your Plex Media Server is in "Program Files (x86)", it's an indicator of running the 32-bit version of Plex Media Server, which is not supported.
|
||||
|
||||
1. Download `IPHLPAPI.dll` from [the latest release](https://github.com/yuv420p10le/plexmediaserver_crack/releases/latest/download/IPHLPAPI.dll) and put it in your Plex Media Server's installation folder. (e.g. `C:\Program Files\Plex\Plex Media Server`)
|
||||
1. Download `IPHLPAPI.dll` from [the latest release](https://gitgud.io/yuv420p10le/plexmediaserver_crack/-/raw/master/binaries/IPHLPAPI.dll) and put it in your Plex Media Server's installation folder. (e.g. `C:\Program Files\Plex\Plex Media Server`)
|
||||
2. Restart Plex Media Server. All features will be unlocked.
|
||||
3. For persistence with updates (as long as the crack doesn't break as a result of them), set the file as read-only.
|
||||
|
||||
## Linux
|
||||
|
||||
x86-64 only.
|
||||
|
||||
First, install `patchelf` from your distribution's package manager or `pip`:
|
||||
### Native
|
||||
|
||||
```bash
|
||||
Install `patchelf`. Can be done either via `pip` or your distribution's package manager.
|
||||
|
||||
```sh
|
||||
sudo apt update && sudo apt install patchelf # Debian/Ubuntu/etc
|
||||
sudo pacman -S patchelf # Arch
|
||||
sudo yum install patchelf # Fedora/RHEL/Centos/Rocky/OpenSUSE/etc
|
||||
@ -25,16 +28,12 @@ sudo apk update && apk add --upgrade patchelf # Alpine
|
||||
sudo pip install patchelf # pip; you might need --break-system-packages if installing if you're on an externally managed environment
|
||||
```
|
||||
|
||||
And then:
|
||||
|
||||
### Native
|
||||
|
||||
The script assumes Plex Media Server is currently running.
|
||||
|
||||
Run the following command:
|
||||
|
||||
```bash
|
||||
sudo sh -c "$(curl -sSL https://github.com/yuv420p10le/plexmediaserver_crack/releases/latest/download/crack_native.sh)"
|
||||
sudo sh -c "$(curl -sSL https://gitgud.io/yuv420p10le/plexmediaserver_crack/-/raw/master/scripts/crack_native.sh)"
|
||||
```
|
||||
|
||||
Your Plex Media Server should now be restarted with all features unlocked.
|
||||
@ -43,6 +42,21 @@ For persistance with Plex updates, create the above as a bash script (run as roo
|
||||
|
||||
### Docker
|
||||
|
||||
First, install a static build of `patchelf`:
|
||||
|
||||
```bash
|
||||
sudo pip install patchelf # pip; you might need --break-system-packages if installing if you're on an externally managed environment
|
||||
|
||||
# Install from provided binaries
|
||||
wget https://github.com/NixOS/patchelf/releases/download/0.18.0/patchelf-0.18.0-x86_64.tar.gz -O /tmp/patchelf.tar.gz && \
|
||||
mkdir /tmp/patchelf && \
|
||||
tar -xvzf /tmp/patchelf.tar.gz -C /tmp/patchelf && \
|
||||
sudo cp -r /tmp/patchelf/ / && \
|
||||
rm -rf /tmp/patchelf
|
||||
```
|
||||
|
||||
Installing `patchelf` from your distribution's package manager will provide you with a dynamically linked binary, which may fail if your Docker image has a too old version of glibc/glibcxx. You can try going with that approach, but do not ask for support if it doesn't work.
|
||||
|
||||
Tested on [linuxserver/docker-plex](https://github.com/linuxserver/docker-plex), but should work for all Docker setups.
|
||||
|
||||
The script assumes Plex Media Server is currently running, that you have a mounted `/config` volume in the container, and that your container is named `plex`.
|
||||
@ -51,7 +65,7 @@ If your container is named differently or if your external volume is mounted els
|
||||
Run the following command: (you can emit `sudo` if the executing user is in the `docker` group)
|
||||
|
||||
```bash
|
||||
sudo sh -c "$(curl -sSL https://github.com/yuv420p10le/plexmediaserver_crack/releases/latest/download/crack_docker.sh)"
|
||||
sudo sh -c "$(curl -sSL https://gitgud.io/yuv420p10le/plexmediaserver_crack/-/raw/master/scripts/crack_docker.sh)"
|
||||
```
|
||||
|
||||
Your Plex Media Server should now be restarted with all features unlocked.
|
||||
@ -59,12 +73,6 @@ Your Plex Media Server should now be restarted with all features unlocked.
|
||||
This will NOT persist through Docker image updates, as rebuilding the container will copy libraries freshly from the image.
|
||||
Setup the above commands as a script for an easy installation, and optionally, set it as a cronjob to run daily.
|
||||
|
||||
|
||||
### Other configurations
|
||||
|
||||
- For an UNRAID (x86-64) setup guide, [check Mizz141's gist](https://gist.github.com/mizz141/608d21fbc2fe4480286c76cc421f40d3).
|
||||
- For binary patching for Windows/Linux (x86-64/ARM64) instead of DLL sideloading/import hijacking, [check edde746's fork](https://github.com/edde746/plexmediaserver_crack).
|
||||
|
||||
### Building
|
||||
|
||||
#### Windows
|
||||
@ -73,13 +81,49 @@ Build using Visual Studio 2022, C++20. You need [Zydis](https://github.com/zyant
|
||||
|
||||
#### Linux
|
||||
|
||||
[linuxserver/docker-plex](https://github.com/linuxserver/docker-plex)'s image uses GLIBC 2.35, and Debian stable (bookworm, as of now) is at 2.36. Use a host with an older version to build (e.g. Debian Bullseye).
|
||||
[linuxserver/docker-plex](https://github.com/linuxserver/docker-plex)'s image uses GLIBC 2.35, and Debian stable (bookworm, as of now) is at 2.36. Use a host with an older version to build (e.g. Debian Bullseye.. or an Alpine container with older glibc such as 2.26).
|
||||
|
||||
`git` `cmake` `make` and a C++ compiler (tested with clang++) required.
|
||||
|
||||
```bash
|
||||
git clone https://gitgud.io/yuv420p10le/plexmediaserver_crack.git && \
|
||||
cd plexmediaserver_crack/linux && \
|
||||
cmake . && \
|
||||
make
|
||||
```
|
||||
|
||||
Alpine:
|
||||
|
||||
```Dockerfile
|
||||
FROM alpine:3.18
|
||||
RUN apk --no-cache add wget ca-certificates libstdc++
|
||||
ARG APK_GLIBC_VERSION=2.26-r0
|
||||
ARG APK_GLIBC_FILE="glibc-${APK_GLIBC_VERSION}.apk"
|
||||
ARG APK_GLIBC_BIN_FILE="glibc-bin-${APK_GLIBC_VERSION}.apk"
|
||||
ARG APK_GLIBC_BASE_URL="https://github.com/sgerrand/alpine-pkg-glibc/releases/download/${APK_GLIBC_VERSION}"
|
||||
RUN wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub \
|
||||
&& wget "${APK_GLIBC_BASE_URL}/${APK_GLIBC_FILE}" \
|
||||
&& apk --force-overwrite --no-cache add "${APK_GLIBC_FILE}" \
|
||||
&& wget "${APK_GLIBC_BASE_URL}/${APK_GLIBC_BIN_FILE}" \
|
||||
&& apk --no-cache add "${APK_GLIBC_BIN_FILE}" \
|
||||
&& rm glibc-*
|
||||
RUN apk add --no-cache cmake make gcc g++ bash
|
||||
WORKDIR /src
|
||||
```
|
||||
|
||||
```bash
|
||||
docker build -t glibc .
|
||||
docker run --rm -v plexmediaserver_crack/linux:/src:rw glibc bash -c "mkdir -p build && cd build && cmake .. && make && chmod -R 777 /src/build"
|
||||
# Binary will be in plexmediaserver_crack/linux/build/plexmediaserver_crack.so
|
||||
```
|
||||
|
||||
`plexmediaserver_crack.so` should now appear in the same directory. Refer to the `scripts/crack_native.sh` or `scripts/crack_docker.sh` scripts to see how it's installed.
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
* For intro/credit detection, go to Settings -> Library -> Marker source; and select the "local detection only" option.
|
||||
* If hardware transcoding (or any other feature) does not work, it should not be related to this repository. Use Google to troubleshoot why said feature doesn't work on your setup specifically.
|
||||
* The "Skip Intro" button will not be displayed on clients that don't have the Plex Pass. It's a client sided limitation. I wrote a crack for the Plex client on Windows to circumvent it, at [plexpass_hook](https://github.com/yuv420p10le/plexpass_hook). The "Skip Credits" button will appear on all clients, including the free ones.
|
||||
* The "Skip Intro" button will not be displayed on clients that don't have the Plex Pass. It's a client sided limitation. I wrote a crack for the Plex client on Windows to circumvent it, at [plexpass_hook](https://gitgud.io/yuv420p10le/plexpass_hook). The "Skip Credits" button will appear on all clients, including the free ones.
|
||||
|
||||
### Screenshots
|
||||
|
||||
|
BIN
binaries/IPHLPAPI.dll
Normal file
BIN
binaries/IPHLPAPI.dll
Normal file
Binary file not shown.
1
binaries/README.md
Normal file
1
binaries/README.md
Normal file
@ -0,0 +1 @@
|
||||
GitLab is god awful and makes life hard when it comes to attaching binaries to a release. I'm just putting them here.
|
BIN
binaries/plexmediaserver_crack.so
Normal file
BIN
binaries/plexmediaserver_crack.so
Normal file
Binary file not shown.
@ -6,6 +6,7 @@
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
#include <bitset>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include "Zydis.h"
|
||||
@ -33,6 +34,7 @@ std::unordered_map<std::string, std::string> g_features =
|
||||
{ "6ab6677b-ad9b-444f-9ca1-b8027d05b3e1", "client-non-destructive-comskip" },
|
||||
{ "5b6190a9-77a4-477e-9fbc-c8118e35a4c1", "client-radio-stations" },
|
||||
{ "65152b75-13a9-408a-bd30-dbd23a259183", "cloudsync" },
|
||||
{ "b25b878c-4f60-4337-9f6b-2d97ef41d036", "cloudflare-turnstile-required" },
|
||||
{ "c4704b28-4e26-460a-bf2e-2576d0c2cb77", "community-new-user-onboarding" },
|
||||
{ "7bb1ed71-a0a3-4362-aa08-7c3fa7241578", "community-friendships-management" },
|
||||
{ "fc3e8322-5e6e-4f4a-9d71-728c6d5656bd", "community-phase0" },
|
||||
@ -55,6 +57,7 @@ std::unordered_map<std::string, std::string> g_features =
|
||||
{ "ce8f644e-87ce-4ba5-b165-fadd69778019", "disable_sharing_friendships" },
|
||||
{ "6225c337-cd26-4ff0-b864-6c6dd84c9e0d", "disco-reported-issues" },
|
||||
{ "d865f64a-ca06-472d-ae01-7a444aba6251", "disco-director-cast-crew-updates" },
|
||||
{ "e9cc7ec1-be5a-4727-af7b-0f107af1a07c", "disco-epg-airings-on-detail-pages" },
|
||||
{ "2131d3dc-56c8-45d0-acec-c4683fd9a027", "discover-genre-browsing" },
|
||||
{ "cb0e4c75-b1cb-43e9-97ea-6b9bc66c717b", "discover-managed-related-vod" },
|
||||
{ "807d9881-a846-40c3-8d54-84fc490b7ba9", "discover-managed-user-test" },
|
||||
@ -119,6 +122,7 @@ std::unordered_map<std::string, std::string> g_features =
|
||||
{ "2ea0e464-ea4f-4be2-97c1-ce6ed4b377dd", "photos-metadata-edition" },
|
||||
{ "850f3d1e-3f38-44c1-9c0c-e3c9127b8b5a", "photosV6-edit" },
|
||||
{ "3a2b0cb6-1519-4431-98e2-823c248c70eb", "photosV6-tv-albums" },
|
||||
{ "068f4adf-43e5-4cc6-b5a1-1243e1be4c53", "playback-speed" },
|
||||
{ "02da2909-ddfd-46be-9e42-65008a79fc05", "played_badges" },
|
||||
{ "9aea4ca5-2095-4619-9339-88c1e662fde6", "pms_health" },
|
||||
{ "222020fb-1504-492d-af33-a0b80a49558a", "premium-dashboard" },
|
||||
@ -156,7 +160,10 @@ std::unordered_map<std::string, std::string> g_features =
|
||||
{ "1dd846ed-7cde-4dc5-8ef6-53d3ce8c4e9d", "visualizers" },
|
||||
{ "bbf73498-4912-4d80-9560-47c4fe212cec", "volume-leveling" },
|
||||
{ "07f804e6-28e6-4beb-b5c3-f2aefc88b938", "tunefind-clients" },
|
||||
{ "9b5a4bea-3bbe-45d2-b226-00a6ef4d8e65", "tvod" },
|
||||
{ "5d80b92d-4ecf-4b0b-935f-5efc907bb2c1", "tvod_playback" },
|
||||
{ "362c5ba7-41e8-400d-8354-18d53868e2d3", "tvod-rentals" },
|
||||
{ "e25d0e25-109e-4d6d-9a54-db0931af31c3", "tvod-wtw" },
|
||||
{ "06d14b9e-2af8-4c2b-a4a1-ea9d5c515824", "two-factor-authentication" },
|
||||
{ "20824f5c-6dd9-4655-9970-e7701a73c02a", "two-factor-authentication-clients" },
|
||||
{ "d14556be-ae6d-4407-89d0-b83953f4789a", "type-first" },
|
||||
@ -170,6 +177,7 @@ std::unordered_map<std::string, std::string> g_features =
|
||||
{ "1b870b8e-f1a7-497c-80b2-857d45f3123f", "vod-schema" },
|
||||
{ "65faa2d0-f57e-4c63-a6b6-f1baa48951b1", "watch-together-20200520" },
|
||||
{ "f83450e2-759a-4de4-8b31-e4a163896d43", "watch-together-invite" },
|
||||
{ "236de47b-a757-4ed7-9003-507b296057b5", "watched-badges-v3" },
|
||||
{ "f0c452ce-11e7-465f-be04-5fb0bf4bec48", "watchlist" },
|
||||
{ "edd6039a-137c-4ace-b5d5-4e111ce9690b", "watchlist-source" },
|
||||
{ "f0f40559-a43a-4b8f-85ef-bdb1de1a912a", "watchlist-rss" },
|
||||
@ -209,8 +217,11 @@ std::unordered_map<std::string, std::string> g_features =
|
||||
{ "a6f3f9b3-c10c-4b94-ad59-755e30ac6c90", "detect-commercials" },
|
||||
};
|
||||
|
||||
std::bitset<704>* g_feature_flags;
|
||||
auto _is_feature_available = reinterpret_cast<decltype(&hook_is_feature_available)>(0);
|
||||
auto _map_find = reinterpret_cast<decltype(&hook_map_find)>(0);
|
||||
auto _bitset_init = reinterpret_cast<decltype(&hook_bitset_init)>(0);
|
||||
auto _is_user_feature_set = reinterpret_cast<decltype(&hook_is_user_feature_set)>(0);
|
||||
|
||||
std::optional<std::tuple<uintptr_t, uintptr_t>> get_dottext_info()
|
||||
{
|
||||
@ -348,9 +359,9 @@ bool process_feature(const char* guid)
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t hook_is_feature_available(uintptr_t user, const char* feature)
|
||||
uint64_t hook_is_feature_available(uintptr_t user, const char** feature)
|
||||
{
|
||||
if(process_feature(feature))
|
||||
if(process_feature(*feature))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -370,6 +381,19 @@ uint64_t* hook_map_find(uintptr_t* rcx, const char** str)
|
||||
return _map_find(rcx, str);
|
||||
}
|
||||
|
||||
uint64_t hook_bitset_init(uintptr_t rcx)
|
||||
{
|
||||
auto ret = _bitset_init(rcx);
|
||||
g_feature_flags->set();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool hook_is_user_feature_set([[maybe_unused]] uintptr_t rcx, [[maybe_unused]] int expected, [[maybe_unused]] int feature)
|
||||
{
|
||||
return static_cast<bool>(expected);
|
||||
}
|
||||
|
||||
void hook()
|
||||
{
|
||||
auto info = get_dottext_info();
|
||||
@ -382,6 +406,32 @@ void hook()
|
||||
const auto start = std::get<0>(info.value());
|
||||
const auto end = std::get<1>(info.value());
|
||||
|
||||
if(const auto is_user_feature_set = sig_scan(start, end, "55 48 89 E5 48 8B 07 48 85 C0 74 6A"); is_user_feature_set)
|
||||
{
|
||||
if(auto trampoline = create_hook(is_user_feature_set.value(), reinterpret_cast<uintptr_t>(hook_is_user_feature_set)); trampoline)
|
||||
{
|
||||
_is_user_feature_set = reinterpret_cast<decltype(_is_user_feature_set)>(trampoline.value());
|
||||
}
|
||||
}
|
||||
|
||||
// Features are now enabled in boost::atomic<std::bitset> as of 2024/08/13 PMS BETA
|
||||
if(const auto bitset = sig_scan(start, end, "48 8D 0D ? ? ? ? 48 8B 94 05 90 FE FF FF"); bitset)
|
||||
{
|
||||
const uintptr_t addr = bitset.value() + 7 + *reinterpret_cast<uint32_t*>(bitset.value() + 3);
|
||||
g_feature_flags = reinterpret_cast<std::bitset<704>*>(addr);
|
||||
|
||||
if(const auto bitset_init = sig_scan(start, end, "55 48 89 E5 41 57 41 56 41 55 41 54 53 48 81 EC ? ? 00 00 49 89 FE 48 8D 9D ? ? ? ? 48 89 DF E8 ? ? ? ? 48 8B 1B 48 85 DB"); bitset_init)
|
||||
{
|
||||
if(auto trampoline = create_hook(bitset_init.value(), reinterpret_cast<uintptr_t>(hook_bitset_init)); trampoline)
|
||||
{
|
||||
_bitset_init = reinterpret_cast<decltype(_bitset_init)>(trampoline.value());
|
||||
|
||||
// No reason to hook the rest
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(const auto is_feature_available_ref = sig_scan(start, end, "E8 ? ? ? ? 86 43"); is_feature_available_ref)
|
||||
{
|
||||
const auto is_feature_available = follow_call_rel32(is_feature_available_ref.value());
|
||||
|
@ -9,6 +9,8 @@ std::optional<std::tuple<uintptr_t, uintptr_t>> get_dottext_info();
|
||||
std::optional<uintptr_t> create_hook(uintptr_t from, uintptr_t to);
|
||||
std::optional<uintptr_t> sig_scan(const uintptr_t start, const uintptr_t end, std::string_view pattern);
|
||||
uintptr_t follow_call_rel32(const uintptr_t address);
|
||||
uint64_t hook_is_feature_available(uintptr_t rcx, const char* guid);
|
||||
uint64_t hook_is_feature_available(uintptr_t rcx, const char** guid);
|
||||
uint64_t* hook_map_find(uintptr_t* rcx, const char** str);
|
||||
uint64_t hook_bitset_init(uintptr_t rcx);
|
||||
bool hook_is_user_feature_set(uintptr_t rcx, int expected, int feature);
|
||||
void hook();
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
PLEX_CONFIG_DIR=/config
|
||||
PLEX_CONTAINER_NAME=plex
|
||||
PLEX_MEDIA_SERVER_DIR=$(ps aux | grep 'Plex Media Server' | grep -v grep | awk '{print $11}' | xargs dirname)
|
||||
PLEX_MEDIA_SERVER_DIR=$(ps aux | grep 'Plex Media Server' | grep -v grep | awk '{print $11}' | xargs dirname | uniq)
|
||||
|
||||
if [ `id -u` -ne 0 ] && ! groups $(whoami) | grep -q '\bdocker\b'; then
|
||||
echo "Run this script as root or through 'sudo'. Alternatively, add your user account to the 'docker' group. Script aborting."
|
||||
@ -20,10 +20,10 @@ fi
|
||||
rm -rf /tmp/plexmediaserver_crack
|
||||
mkdir /tmp/plexmediaserver_crack
|
||||
cd /tmp/plexmediaserver_crack
|
||||
wget https://github.com/yuv420p10le/plexmediaserver_crack/releases/latest/download/plexmediaserver_crack.so
|
||||
docker cp $(which patchelf) $PLEX_CONTAINER_NAME:$PLEX_CONFIG_DIR
|
||||
docker cp plexmediaserver_crack.so $PLEX_CONTAINER_NAME:$PLEX_CONFIG_DIR
|
||||
docker exec -it $PLEX_CONTAINER_NAME ln -s /config/plexmediaserver_crack.so $PLEX_MEDIA_SERVER_DIR/lib/plexmediaserver_crack.so
|
||||
docker exec -it $PLEX_CONTAINER_NAME /config/patchelf --remove-needed plexmediaserver_crack.so $PLEX_MEDIA_SERVER_DIR/lib/libsoci_core.so
|
||||
docker exec -it $PLEX_CONTAINER_NAME /config/patchelf --add-needed plexmediaserver_crack.so $PLEX_MEDIA_SERVER_DIR/lib/libsoci_core.so
|
||||
wget https://gitgud.io/yuv420p10le/plexmediaserver_crack/-/raw/master/binaries/plexmediaserver_crack.so
|
||||
docker cp $(which patchelf) $PLEX_CONTAINER_NAME:$PLEX_CONFIG_DIR/patchelf
|
||||
docker cp plexmediaserver_crack.so $PLEX_CONTAINER_NAME:$PLEX_CONFIG_DIR/plexmediaserver_crack.so
|
||||
docker exec $PLEX_CONTAINER_NAME ln -sf /config/plexmediaserver_crack.so $PLEX_MEDIA_SERVER_DIR/lib/plexmediaserver_crack.so
|
||||
docker exec $PLEX_CONTAINER_NAME /config/patchelf --remove-needed plexmediaserver_crack.so $PLEX_MEDIA_SERVER_DIR/lib/libsoci_core.so
|
||||
docker exec $PLEX_CONTAINER_NAME /config/patchelf --add-needed plexmediaserver_crack.so $PLEX_MEDIA_SERVER_DIR/lib/libsoci_core.so
|
||||
docker restart $PLEX_CONTAINER_NAME
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
# The script assumes Plex Media Server is currently running.
|
||||
|
||||
PLEX_MEDIA_SERVER_DIR=$(ps aux | grep 'Plex Media Server' | grep -v grep | awk '{print $11}' | xargs dirname)
|
||||
PLEX_MEDIA_SERVER_DIR=$(ps aux | grep 'Plex Media Server' | grep -v grep | awk '{print $11}' | xargs dirname | uniq)
|
||||
|
||||
if [ `id -u` -ne 0 ]; then
|
||||
echo "Run this script as root or with sudo. Script aborting."
|
||||
@ -17,9 +17,9 @@ fi
|
||||
rm -rf /opt/plexmediaserver_crack
|
||||
mkdir /opt/plexmediaserver_crack
|
||||
cd /opt/plexmediaserver_crack
|
||||
wget https://github.com/yuv420p10le/plexmediaserver_crack/releases/latest/download/plexmediaserver_crack.so
|
||||
wget https://gitgud.io/yuv420p10le/plexmediaserver_crack/-/raw/master/binaries/plexmediaserver_crack.so
|
||||
rm $PLEX_MEDIA_SERVER_DIR/lib/plexmediaserver_crack.so
|
||||
ln -s /opt/plexmediaserver_crack/plexmediaserver_crack.so $PLEX_MEDIA_SERVER_DIR/lib/plexmediaserver_crack.so
|
||||
ln -sf /opt/plexmediaserver_crack/plexmediaserver_crack.so $PLEX_MEDIA_SERVER_DIR/lib/plexmediaserver_crack.so
|
||||
patchelf --remove-needed plexmediaserver_crack.so $PLEX_MEDIA_SERVER_DIR/lib/libsoci_core.so
|
||||
patchelf --add-needed plexmediaserver_crack.so $PLEX_MEDIA_SERVER_DIR/lib/libsoci_core.so
|
||||
systemctl restart plexmediaserver
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <regex>
|
||||
#include <print>
|
||||
#endif
|
||||
#include <bitset>
|
||||
|
||||
std::unordered_map<std::string, std::string> g_features =
|
||||
{
|
||||
@ -40,6 +41,7 @@ std::unordered_map<std::string, std::string> g_features =
|
||||
{ "6ab6677b-ad9b-444f-9ca1-b8027d05b3e1", "client-non-destructive-comskip" },
|
||||
{ "5b6190a9-77a4-477e-9fbc-c8118e35a4c1", "client-radio-stations" },
|
||||
{ "65152b75-13a9-408a-bd30-dbd23a259183", "cloudsync" },
|
||||
{ "b25b878c-4f60-4337-9f6b-2d97ef41d036", "cloudflare-turnstile-required" },
|
||||
{ "c4704b28-4e26-460a-bf2e-2576d0c2cb77", "community-new-user-onboarding" },
|
||||
{ "7bb1ed71-a0a3-4362-aa08-7c3fa7241578", "community-friendships-management" },
|
||||
{ "fc3e8322-5e6e-4f4a-9d71-728c6d5656bd", "community-phase0" },
|
||||
@ -49,6 +51,7 @@ std::unordered_map<std::string, std::string> g_features =
|
||||
{ "06798ab7-4fa5-4416-9db3-f313c4292f01", "community-p2-r2" },
|
||||
{ "626004b8-a8b8-4fb1-9adc-8a6277f98597", "community-p2-r3" },
|
||||
{ "8f47a689-aa84-408f-bf6b-00015e9413e1", "community-p2-r4" },
|
||||
{ "ad57040a-0b38-4862-bfcf-3fe116e9767b", "community-p3r1-reactions" },
|
||||
{ "7f46bf17-fabf-4f96-99a2-cf374f6eed71", "comments_and_replies_push_notifications" },
|
||||
{ "c36a6985-eee3-4400-a394-c5787fad15b5", "friend_request_push_notifications" },
|
||||
{ "3f6baa76-7488-479a-9e4f-49ff2c0d3711", "community_access_plex_tv" },
|
||||
@ -62,6 +65,7 @@ std::unordered_map<std::string, std::string> g_features =
|
||||
{ "ce8f644e-87ce-4ba5-b165-fadd69778019", "disable_sharing_friendships" },
|
||||
{ "6225c337-cd26-4ff0-b864-6c6dd84c9e0d", "disco-reported-issues" },
|
||||
{ "d865f64a-ca06-472d-ae01-7a444aba6251", "disco-director-cast-crew-updates" },
|
||||
{ "e9cc7ec1-be5a-4727-af7b-0f107af1a07c", "disco-epg-airings-on-detail-pages" },
|
||||
{ "2131d3dc-56c8-45d0-acec-c4683fd9a027", "discover-genre-browsing" },
|
||||
{ "cb0e4c75-b1cb-43e9-97ea-6b9bc66c717b", "discover-managed-related-vod" },
|
||||
{ "807d9881-a846-40c3-8d54-84fc490b7ba9", "discover-managed-user-test" },
|
||||
@ -126,6 +130,7 @@ std::unordered_map<std::string, std::string> g_features =
|
||||
{ "2ea0e464-ea4f-4be2-97c1-ce6ed4b377dd", "photos-metadata-edition" },
|
||||
{ "850f3d1e-3f38-44c1-9c0c-e3c9127b8b5a", "photosV6-edit" },
|
||||
{ "3a2b0cb6-1519-4431-98e2-823c248c70eb", "photosV6-tv-albums" },
|
||||
{ "068f4adf-43e5-4cc6-b5a1-1243e1be4c53", "playback-speed" },
|
||||
{ "02da2909-ddfd-46be-9e42-65008a79fc05", "played_badges" },
|
||||
{ "9aea4ca5-2095-4619-9339-88c1e662fde6", "pms_health" },
|
||||
{ "222020fb-1504-492d-af33-a0b80a49558a", "premium-dashboard" },
|
||||
@ -134,7 +139,6 @@ std::unordered_map<std::string, std::string> g_features =
|
||||
{ "3eb2789b-200c-4a15-91d2-dedfe560953c", "rate-limit-client-token" },
|
||||
{ "64adaa4e-aa7e-457d-b385-51438216d7fe", "shared_server_notification" },
|
||||
{ "6c4d66d9-729d-49dc-b70d-ab2652abf15a", "shared_source_notification" },
|
||||
{ "0cce52a7-0778-4781-9a07-712370fb6b8a", "require-plex-nonce" },
|
||||
{ "644c4466-05fa-45e0-a478-c594cf81778f", "save-to-library" },
|
||||
{ "ccef9d3a-537a-43d9-8161-4c7113c6e2bb", "scrobbling-service" },
|
||||
{ "7b392594-6949-4736-9894-e57a9dfe4037", "scrobbling-service-plex-tv" },
|
||||
@ -164,6 +168,8 @@ std::unordered_map<std::string, std::string> g_features =
|
||||
{ "bbf73498-4912-4d80-9560-47c4fe212cec", "volume-leveling" },
|
||||
{ "07f804e6-28e6-4beb-b5c3-f2aefc88b938", "tunefind-clients" },
|
||||
{ "5d80b92d-4ecf-4b0b-935f-5efc907bb2c1", "tvod_playback" },
|
||||
{ "362c5ba7-41e8-400d-8354-18d53868e2d3", "tvod-rentals" },
|
||||
{ "e25d0e25-109e-4d6d-9a54-db0931af31c3", "tvod-wtw" },
|
||||
{ "06d14b9e-2af8-4c2b-a4a1-ea9d5c515824", "two-factor-authentication" },
|
||||
{ "20824f5c-6dd9-4655-9970-e7701a73c02a", "two-factor-authentication-clients" },
|
||||
{ "d14556be-ae6d-4407-89d0-b83953f4789a", "type-first" },
|
||||
@ -177,6 +183,7 @@ std::unordered_map<std::string, std::string> g_features =
|
||||
{ "1b870b8e-f1a7-497c-80b2-857d45f3123f", "vod-schema" },
|
||||
{ "65faa2d0-f57e-4c63-a6b6-f1baa48951b1", "watch-together-20200520" },
|
||||
{ "f83450e2-759a-4de4-8b31-e4a163896d43", "watch-together-invite" },
|
||||
{ "236de47b-a757-4ed7-9003-507b296057b5", "watched-badges-v3" },
|
||||
{ "f0c452ce-11e7-465f-be04-5fb0bf4bec48", "watchlist" },
|
||||
{ "edd6039a-137c-4ace-b5d5-4e111ce9690b", "watchlist-source" },
|
||||
{ "f0f40559-a43a-4b8f-85ef-bdb1de1a912a", "watchlist-rss" },
|
||||
@ -239,9 +246,13 @@ std::unordered_set<std::string> g_ignored_guids
|
||||
"a548af72-b804-4d05-8569-52785952d31d", // Unknown, used in LibraryRequestHandler
|
||||
};
|
||||
|
||||
std::bitset<416>* g_feature_flags;
|
||||
|
||||
SafetyHookInline _is_feature_available{};
|
||||
SafetyHookInline _map_find{};
|
||||
SafetyHookInline _is_user_feature_set{};
|
||||
SafetyHookInline _feature_manager_init{};
|
||||
SafetyHookInline _bitset_init{};
|
||||
auto _feature_manager = reinterpret_cast<FeatureManager*>(0);
|
||||
|
||||
uintptr_t get_current_process_handle()
|
||||
@ -366,9 +377,11 @@ bool process_feature(const char* guid, [[maybe_unused]] uintptr_t caller)
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t hook_is_feature_available(uintptr_t user, const char* feature)
|
||||
uint64_t hook_is_feature_available(uintptr_t user, str_holder* feature)
|
||||
{
|
||||
if(process_feature(feature, reinterpret_cast<uintptr_t>(_ReturnAddress()) - get_current_process_handle()))
|
||||
std::string_view str(feature->obj.length >= 16 ? feature->obj.str : feature->str);
|
||||
|
||||
if(process_feature(str.data(), reinterpret_cast<uintptr_t>(_ReturnAddress()) - get_current_process_handle()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -376,10 +389,12 @@ uint64_t hook_is_feature_available(uintptr_t user, const char* feature)
|
||||
return _is_feature_available.call<uint64_t>(user, feature);
|
||||
}
|
||||
|
||||
uint64_t* hook_map_find(uintptr_t* rcx, uintptr_t rdx, const char** str)
|
||||
uint64_t* hook_map_find(uintptr_t* rcx, uintptr_t rdx, str_holder* str)
|
||||
{
|
||||
std::string_view feature(str->obj.length >= 16 ? str->obj.str : str->str);
|
||||
|
||||
if(_feature_manager != nullptr && rcx == &_feature_manager->m_feature_map() &&
|
||||
process_feature(*str, reinterpret_cast<uintptr_t>(_ReturnAddress()) - get_current_process_handle()))
|
||||
process_feature(feature.data(), reinterpret_cast<uintptr_t>(_ReturnAddress()) - get_current_process_handle()))
|
||||
{
|
||||
static uint64_t FAKE_PTR = 0;
|
||||
|
||||
@ -389,6 +404,25 @@ uint64_t* hook_map_find(uintptr_t* rcx, uintptr_t rdx, const char** str)
|
||||
return _map_find.call<uint64_t*>(rcx, rdx, str);
|
||||
}
|
||||
|
||||
void hook_bitset_init(uintptr_t rcx, uintptr_t rdx)
|
||||
{
|
||||
_bitset_init.call<void>(rcx, rdx);
|
||||
g_feature_flags->set();
|
||||
|
||||
#if _DEBUG
|
||||
std::println("[INFO] [plexmediaserver_crack] Forced feature flags on {:08X}.", reinterpret_cast<uintptr_t>(g_feature_flags));
|
||||
#endif
|
||||
}
|
||||
|
||||
uint64_t hook_is_user_feature_set([[maybe_unused]] int expected, [[maybe_unused]] int feature)
|
||||
{
|
||||
#if _DEBUG
|
||||
std::println("[INFO] [plexmediaserver_crack] Spoofed feature {} to {}.", feature, expected);
|
||||
#endif
|
||||
|
||||
return expected;
|
||||
}
|
||||
|
||||
FeatureManager* hook_feature_manager_init(FeatureManager* rcx)
|
||||
{
|
||||
return _feature_manager = _feature_manager_init.call<FeatureManager*>(rcx);
|
||||
@ -414,6 +448,25 @@ void hook()
|
||||
|
||||
const auto start = std::get<0>(info.value());
|
||||
const auto end = std::get<1>(info.value());
|
||||
const auto bitset = sig_scan(start, end, "8B 84 24 ? ? 00 00 87 05 ? ? ? ? 8B");
|
||||
const auto bitset_init = sig_scan(start, end, "48 89 5C 24 18 48 89 74 24 20 57 41 54 41 55 41 56 41 57 48 81 EC 90 02 00 00 48 8B 05 ? ? ? ? 48 33 C4 48 89 84 24 88");
|
||||
const auto is_user_feature_set = sig_scan(start, end, "48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 48 89 7C 24 20 41 56 48 83 EC 30 48 63");
|
||||
|
||||
if(is_user_feature_set)
|
||||
{
|
||||
_is_user_feature_set = safetyhook::create_inline(reinterpret_cast<void*>(is_user_feature_set.value()), reinterpret_cast<void*>(hook_is_user_feature_set));
|
||||
}
|
||||
|
||||
// Features are now enabled in std::atomic<std::bitset> as of 2024/08/13 PMS BETA
|
||||
if(bitset && bitset_init)
|
||||
{
|
||||
const uintptr_t addr = bitset.value() + 5 + *reinterpret_cast<uint32_t*>(bitset.value() + 9);
|
||||
g_feature_flags = reinterpret_cast<std::bitset<416>*>(addr + sizeof(uintptr_t));
|
||||
_bitset_init = safetyhook::create_inline(reinterpret_cast<void*>(bitset_init.value()), reinterpret_cast<void*>(hook_bitset_init));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const auto is_feature_available = sig_scan(start, end, "41 54 41 56 41 57 48 83 EC 20 4C 8B F9 4C 8B F2");
|
||||
const auto map_find = sig_scan(start, end, "48 8B C4 55 41 55");
|
||||
const auto feature_manager_init = sig_scan(start, end, "48 89 5C 24 10 48 89 74 24 18 48 89 7C 24 20 55 41 54 41 55 41 56 41 57 48 8D AC 24 B0 EB");
|
||||
@ -422,7 +475,7 @@ void hook()
|
||||
if((!map_find && !is_feature_available) || !feature_manager_init || !feature_map_offset)
|
||||
{
|
||||
#if _DEBUG
|
||||
std::println("[ERR] [plexmediaserver_crack] Couldn't find either is_feature_enabled or std::map<std::string, std::vector<float>>::find, FeatureManaget::Init, or the feature map offset; aborting.");
|
||||
std::println("[ERR] [plexmediaserver_crack] Couldn't find either is_feature_enabled or std::map<std::string, std::vector<float>>::find, FeatureManager::Init, or the feature map offset; aborting.");
|
||||
#endif
|
||||
|
||||
return;
|
||||
|
@ -24,10 +24,24 @@ public:
|
||||
member_at(uintptr_t, m_map_offset, m_feature_map);
|
||||
};
|
||||
|
||||
union str_holder
|
||||
{
|
||||
struct
|
||||
{
|
||||
const char* str;
|
||||
uintptr_t pad[2];
|
||||
size_t length;
|
||||
} obj;
|
||||
|
||||
const char str[16];
|
||||
};
|
||||
|
||||
uintptr_t get_current_process_handle();
|
||||
std::optional<std::tuple<uintptr_t, uintptr_t>> get_section_info(std::string_view name);
|
||||
std::optional<uintptr_t> sig_scan(const uintptr_t start, const uintptr_t end, std::string_view pattern);
|
||||
uint64_t hook_is_feature_available(uintptr_t rcx, const char* guid);
|
||||
uint64_t* hook_map_find(uintptr_t* rcx, uintptr_t rdx, const char** str);
|
||||
uint64_t hook_is_feature_available(uintptr_t rcx, str_holder* guid);
|
||||
uint64_t* hook_map_find(uintptr_t* rcx, uintptr_t rdx, str_holder* str);
|
||||
void hook_bitset_init(uintptr_t rcx, uintptr_t rdx);
|
||||
uint64_t hook_is_user_feature_set(int expected, int feature);
|
||||
FeatureManager* hook_feature_manager_init(FeatureManager* rcx);
|
||||
void hook();
|
||||
|
Loading…
x
Reference in New Issue
Block a user