mirror of
https://gitgud.io/yuv420p10le/plexmediaserver_crack
synced 2025-07-06 01:12:25 +00:00
Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1906022184 | ||
![]() |
a3001d1550 | ||
![]() |
1209483a5b | ||
![]() |
36046155d1 | ||
![]() |
7d5a79c019 | ||
![]() |
d4f1d18bac | ||
![]() |
c5ef9d2c63 | ||
![]() |
d9ec635fbd |
31
README.md
31
README.md
@ -81,24 +81,49 @@ Build using Visual Studio 2022, C++20. You need [Zydis](https://github.com/zyant
|
|||||||
|
|
||||||
#### Linux
|
#### 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.
|
`git` `cmake` `make` and a C++ compiler (tested with clang++) required.
|
||||||
|
|
||||||
```
|
```bash
|
||||||
git clone https://gitgud.io/yuv420p10le/plexmediaserver_crack.git && \
|
git clone https://gitgud.io/yuv420p10le/plexmediaserver_crack.git && \
|
||||||
cd plexmediaserver_crack/linux && \
|
cd plexmediaserver_crack/linux && \
|
||||||
cmake . && \
|
cmake . && \
|
||||||
make
|
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.
|
`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
|
### Troubleshooting
|
||||||
|
|
||||||
* For intro/credit detection, go to Settings -> Library -> Marker source; and select the "local detection only" option.
|
* 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.
|
* 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
|
### Screenshots
|
||||||
|
|
||||||
|
Binary file not shown.
Binary file not shown.
@ -6,6 +6,7 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <bitset>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include "Zydis.h"
|
#include "Zydis.h"
|
||||||
@ -56,6 +57,7 @@ std::unordered_map<std::string, std::string> g_features =
|
|||||||
{ "ce8f644e-87ce-4ba5-b165-fadd69778019", "disable_sharing_friendships" },
|
{ "ce8f644e-87ce-4ba5-b165-fadd69778019", "disable_sharing_friendships" },
|
||||||
{ "6225c337-cd26-4ff0-b864-6c6dd84c9e0d", "disco-reported-issues" },
|
{ "6225c337-cd26-4ff0-b864-6c6dd84c9e0d", "disco-reported-issues" },
|
||||||
{ "d865f64a-ca06-472d-ae01-7a444aba6251", "disco-director-cast-crew-updates" },
|
{ "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" },
|
{ "2131d3dc-56c8-45d0-acec-c4683fd9a027", "discover-genre-browsing" },
|
||||||
{ "cb0e4c75-b1cb-43e9-97ea-6b9bc66c717b", "discover-managed-related-vod" },
|
{ "cb0e4c75-b1cb-43e9-97ea-6b9bc66c717b", "discover-managed-related-vod" },
|
||||||
{ "807d9881-a846-40c3-8d54-84fc490b7ba9", "discover-managed-user-test" },
|
{ "807d9881-a846-40c3-8d54-84fc490b7ba9", "discover-managed-user-test" },
|
||||||
@ -120,6 +122,7 @@ std::unordered_map<std::string, std::string> g_features =
|
|||||||
{ "2ea0e464-ea4f-4be2-97c1-ce6ed4b377dd", "photos-metadata-edition" },
|
{ "2ea0e464-ea4f-4be2-97c1-ce6ed4b377dd", "photos-metadata-edition" },
|
||||||
{ "850f3d1e-3f38-44c1-9c0c-e3c9127b8b5a", "photosV6-edit" },
|
{ "850f3d1e-3f38-44c1-9c0c-e3c9127b8b5a", "photosV6-edit" },
|
||||||
{ "3a2b0cb6-1519-4431-98e2-823c248c70eb", "photosV6-tv-albums" },
|
{ "3a2b0cb6-1519-4431-98e2-823c248c70eb", "photosV6-tv-albums" },
|
||||||
|
{ "068f4adf-43e5-4cc6-b5a1-1243e1be4c53", "playback-speed" },
|
||||||
{ "02da2909-ddfd-46be-9e42-65008a79fc05", "played_badges" },
|
{ "02da2909-ddfd-46be-9e42-65008a79fc05", "played_badges" },
|
||||||
{ "9aea4ca5-2095-4619-9339-88c1e662fde6", "pms_health" },
|
{ "9aea4ca5-2095-4619-9339-88c1e662fde6", "pms_health" },
|
||||||
{ "222020fb-1504-492d-af33-a0b80a49558a", "premium-dashboard" },
|
{ "222020fb-1504-492d-af33-a0b80a49558a", "premium-dashboard" },
|
||||||
@ -160,6 +163,7 @@ std::unordered_map<std::string, std::string> g_features =
|
|||||||
{ "9b5a4bea-3bbe-45d2-b226-00a6ef4d8e65", "tvod" },
|
{ "9b5a4bea-3bbe-45d2-b226-00a6ef4d8e65", "tvod" },
|
||||||
{ "5d80b92d-4ecf-4b0b-935f-5efc907bb2c1", "tvod_playback" },
|
{ "5d80b92d-4ecf-4b0b-935f-5efc907bb2c1", "tvod_playback" },
|
||||||
{ "362c5ba7-41e8-400d-8354-18d53868e2d3", "tvod-rentals" },
|
{ "362c5ba7-41e8-400d-8354-18d53868e2d3", "tvod-rentals" },
|
||||||
|
{ "e25d0e25-109e-4d6d-9a54-db0931af31c3", "tvod-wtw" },
|
||||||
{ "06d14b9e-2af8-4c2b-a4a1-ea9d5c515824", "two-factor-authentication" },
|
{ "06d14b9e-2af8-4c2b-a4a1-ea9d5c515824", "two-factor-authentication" },
|
||||||
{ "20824f5c-6dd9-4655-9970-e7701a73c02a", "two-factor-authentication-clients" },
|
{ "20824f5c-6dd9-4655-9970-e7701a73c02a", "two-factor-authentication-clients" },
|
||||||
{ "d14556be-ae6d-4407-89d0-b83953f4789a", "type-first" },
|
{ "d14556be-ae6d-4407-89d0-b83953f4789a", "type-first" },
|
||||||
@ -173,6 +177,7 @@ std::unordered_map<std::string, std::string> g_features =
|
|||||||
{ "1b870b8e-f1a7-497c-80b2-857d45f3123f", "vod-schema" },
|
{ "1b870b8e-f1a7-497c-80b2-857d45f3123f", "vod-schema" },
|
||||||
{ "65faa2d0-f57e-4c63-a6b6-f1baa48951b1", "watch-together-20200520" },
|
{ "65faa2d0-f57e-4c63-a6b6-f1baa48951b1", "watch-together-20200520" },
|
||||||
{ "f83450e2-759a-4de4-8b31-e4a163896d43", "watch-together-invite" },
|
{ "f83450e2-759a-4de4-8b31-e4a163896d43", "watch-together-invite" },
|
||||||
|
{ "236de47b-a757-4ed7-9003-507b296057b5", "watched-badges-v3" },
|
||||||
{ "f0c452ce-11e7-465f-be04-5fb0bf4bec48", "watchlist" },
|
{ "f0c452ce-11e7-465f-be04-5fb0bf4bec48", "watchlist" },
|
||||||
{ "edd6039a-137c-4ace-b5d5-4e111ce9690b", "watchlist-source" },
|
{ "edd6039a-137c-4ace-b5d5-4e111ce9690b", "watchlist-source" },
|
||||||
{ "f0f40559-a43a-4b8f-85ef-bdb1de1a912a", "watchlist-rss" },
|
{ "f0f40559-a43a-4b8f-85ef-bdb1de1a912a", "watchlist-rss" },
|
||||||
@ -212,8 +217,11 @@ std::unordered_map<std::string, std::string> g_features =
|
|||||||
{ "a6f3f9b3-c10c-4b94-ad59-755e30ac6c90", "detect-commercials" },
|
{ "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 _is_feature_available = reinterpret_cast<decltype(&hook_is_feature_available)>(0);
|
||||||
auto _map_find = reinterpret_cast<decltype(&hook_map_find)>(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()
|
std::optional<std::tuple<uintptr_t, uintptr_t>> get_dottext_info()
|
||||||
{
|
{
|
||||||
@ -373,6 +381,19 @@ uint64_t* hook_map_find(uintptr_t* rcx, const char** str)
|
|||||||
return _map_find(rcx, 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()
|
void hook()
|
||||||
{
|
{
|
||||||
auto info = get_dottext_info();
|
auto info = get_dottext_info();
|
||||||
@ -385,6 +406,32 @@ void hook()
|
|||||||
const auto start = std::get<0>(info.value());
|
const auto start = std::get<0>(info.value());
|
||||||
const auto end = std::get<1>(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)
|
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());
|
const auto is_feature_available = follow_call_rel32(is_feature_available_ref.value());
|
||||||
|
@ -11,4 +11,6 @@ std::optional<uintptr_t> sig_scan(const uintptr_t start, const uintptr_t end, st
|
|||||||
uintptr_t follow_call_rel32(const uintptr_t address);
|
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_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();
|
void hook();
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include <regex>
|
#include <regex>
|
||||||
#include <print>
|
#include <print>
|
||||||
#endif
|
#endif
|
||||||
|
#include <bitset>
|
||||||
|
|
||||||
std::unordered_map<std::string, std::string> g_features =
|
std::unordered_map<std::string, std::string> g_features =
|
||||||
{
|
{
|
||||||
@ -50,6 +51,7 @@ std::unordered_map<std::string, std::string> g_features =
|
|||||||
{ "06798ab7-4fa5-4416-9db3-f313c4292f01", "community-p2-r2" },
|
{ "06798ab7-4fa5-4416-9db3-f313c4292f01", "community-p2-r2" },
|
||||||
{ "626004b8-a8b8-4fb1-9adc-8a6277f98597", "community-p2-r3" },
|
{ "626004b8-a8b8-4fb1-9adc-8a6277f98597", "community-p2-r3" },
|
||||||
{ "8f47a689-aa84-408f-bf6b-00015e9413e1", "community-p2-r4" },
|
{ "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" },
|
{ "7f46bf17-fabf-4f96-99a2-cf374f6eed71", "comments_and_replies_push_notifications" },
|
||||||
{ "c36a6985-eee3-4400-a394-c5787fad15b5", "friend_request_push_notifications" },
|
{ "c36a6985-eee3-4400-a394-c5787fad15b5", "friend_request_push_notifications" },
|
||||||
{ "3f6baa76-7488-479a-9e4f-49ff2c0d3711", "community_access_plex_tv" },
|
{ "3f6baa76-7488-479a-9e4f-49ff2c0d3711", "community_access_plex_tv" },
|
||||||
@ -63,6 +65,7 @@ std::unordered_map<std::string, std::string> g_features =
|
|||||||
{ "ce8f644e-87ce-4ba5-b165-fadd69778019", "disable_sharing_friendships" },
|
{ "ce8f644e-87ce-4ba5-b165-fadd69778019", "disable_sharing_friendships" },
|
||||||
{ "6225c337-cd26-4ff0-b864-6c6dd84c9e0d", "disco-reported-issues" },
|
{ "6225c337-cd26-4ff0-b864-6c6dd84c9e0d", "disco-reported-issues" },
|
||||||
{ "d865f64a-ca06-472d-ae01-7a444aba6251", "disco-director-cast-crew-updates" },
|
{ "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" },
|
{ "2131d3dc-56c8-45d0-acec-c4683fd9a027", "discover-genre-browsing" },
|
||||||
{ "cb0e4c75-b1cb-43e9-97ea-6b9bc66c717b", "discover-managed-related-vod" },
|
{ "cb0e4c75-b1cb-43e9-97ea-6b9bc66c717b", "discover-managed-related-vod" },
|
||||||
{ "807d9881-a846-40c3-8d54-84fc490b7ba9", "discover-managed-user-test" },
|
{ "807d9881-a846-40c3-8d54-84fc490b7ba9", "discover-managed-user-test" },
|
||||||
@ -127,6 +130,7 @@ std::unordered_map<std::string, std::string> g_features =
|
|||||||
{ "2ea0e464-ea4f-4be2-97c1-ce6ed4b377dd", "photos-metadata-edition" },
|
{ "2ea0e464-ea4f-4be2-97c1-ce6ed4b377dd", "photos-metadata-edition" },
|
||||||
{ "850f3d1e-3f38-44c1-9c0c-e3c9127b8b5a", "photosV6-edit" },
|
{ "850f3d1e-3f38-44c1-9c0c-e3c9127b8b5a", "photosV6-edit" },
|
||||||
{ "3a2b0cb6-1519-4431-98e2-823c248c70eb", "photosV6-tv-albums" },
|
{ "3a2b0cb6-1519-4431-98e2-823c248c70eb", "photosV6-tv-albums" },
|
||||||
|
{ "068f4adf-43e5-4cc6-b5a1-1243e1be4c53", "playback-speed" },
|
||||||
{ "02da2909-ddfd-46be-9e42-65008a79fc05", "played_badges" },
|
{ "02da2909-ddfd-46be-9e42-65008a79fc05", "played_badges" },
|
||||||
{ "9aea4ca5-2095-4619-9339-88c1e662fde6", "pms_health" },
|
{ "9aea4ca5-2095-4619-9339-88c1e662fde6", "pms_health" },
|
||||||
{ "222020fb-1504-492d-af33-a0b80a49558a", "premium-dashboard" },
|
{ "222020fb-1504-492d-af33-a0b80a49558a", "premium-dashboard" },
|
||||||
@ -135,7 +139,6 @@ std::unordered_map<std::string, std::string> g_features =
|
|||||||
{ "3eb2789b-200c-4a15-91d2-dedfe560953c", "rate-limit-client-token" },
|
{ "3eb2789b-200c-4a15-91d2-dedfe560953c", "rate-limit-client-token" },
|
||||||
{ "64adaa4e-aa7e-457d-b385-51438216d7fe", "shared_server_notification" },
|
{ "64adaa4e-aa7e-457d-b385-51438216d7fe", "shared_server_notification" },
|
||||||
{ "6c4d66d9-729d-49dc-b70d-ab2652abf15a", "shared_source_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" },
|
{ "644c4466-05fa-45e0-a478-c594cf81778f", "save-to-library" },
|
||||||
{ "ccef9d3a-537a-43d9-8161-4c7113c6e2bb", "scrobbling-service" },
|
{ "ccef9d3a-537a-43d9-8161-4c7113c6e2bb", "scrobbling-service" },
|
||||||
{ "7b392594-6949-4736-9894-e57a9dfe4037", "scrobbling-service-plex-tv" },
|
{ "7b392594-6949-4736-9894-e57a9dfe4037", "scrobbling-service-plex-tv" },
|
||||||
@ -164,9 +167,9 @@ std::unordered_map<std::string, std::string> g_features =
|
|||||||
{ "1dd846ed-7cde-4dc5-8ef6-53d3ce8c4e9d", "visualizers" },
|
{ "1dd846ed-7cde-4dc5-8ef6-53d3ce8c4e9d", "visualizers" },
|
||||||
{ "bbf73498-4912-4d80-9560-47c4fe212cec", "volume-leveling" },
|
{ "bbf73498-4912-4d80-9560-47c4fe212cec", "volume-leveling" },
|
||||||
{ "07f804e6-28e6-4beb-b5c3-f2aefc88b938", "tunefind-clients" },
|
{ "07f804e6-28e6-4beb-b5c3-f2aefc88b938", "tunefind-clients" },
|
||||||
{ "9b5a4bea-3bbe-45d2-b226-00a6ef4d8e65", "tvod" },
|
|
||||||
{ "5d80b92d-4ecf-4b0b-935f-5efc907bb2c1", "tvod_playback" },
|
{ "5d80b92d-4ecf-4b0b-935f-5efc907bb2c1", "tvod_playback" },
|
||||||
{ "362c5ba7-41e8-400d-8354-18d53868e2d3", "tvod-rentals" },
|
{ "362c5ba7-41e8-400d-8354-18d53868e2d3", "tvod-rentals" },
|
||||||
|
{ "e25d0e25-109e-4d6d-9a54-db0931af31c3", "tvod-wtw" },
|
||||||
{ "06d14b9e-2af8-4c2b-a4a1-ea9d5c515824", "two-factor-authentication" },
|
{ "06d14b9e-2af8-4c2b-a4a1-ea9d5c515824", "two-factor-authentication" },
|
||||||
{ "20824f5c-6dd9-4655-9970-e7701a73c02a", "two-factor-authentication-clients" },
|
{ "20824f5c-6dd9-4655-9970-e7701a73c02a", "two-factor-authentication-clients" },
|
||||||
{ "d14556be-ae6d-4407-89d0-b83953f4789a", "type-first" },
|
{ "d14556be-ae6d-4407-89d0-b83953f4789a", "type-first" },
|
||||||
@ -180,6 +183,7 @@ std::unordered_map<std::string, std::string> g_features =
|
|||||||
{ "1b870b8e-f1a7-497c-80b2-857d45f3123f", "vod-schema" },
|
{ "1b870b8e-f1a7-497c-80b2-857d45f3123f", "vod-schema" },
|
||||||
{ "65faa2d0-f57e-4c63-a6b6-f1baa48951b1", "watch-together-20200520" },
|
{ "65faa2d0-f57e-4c63-a6b6-f1baa48951b1", "watch-together-20200520" },
|
||||||
{ "f83450e2-759a-4de4-8b31-e4a163896d43", "watch-together-invite" },
|
{ "f83450e2-759a-4de4-8b31-e4a163896d43", "watch-together-invite" },
|
||||||
|
{ "236de47b-a757-4ed7-9003-507b296057b5", "watched-badges-v3" },
|
||||||
{ "f0c452ce-11e7-465f-be04-5fb0bf4bec48", "watchlist" },
|
{ "f0c452ce-11e7-465f-be04-5fb0bf4bec48", "watchlist" },
|
||||||
{ "edd6039a-137c-4ace-b5d5-4e111ce9690b", "watchlist-source" },
|
{ "edd6039a-137c-4ace-b5d5-4e111ce9690b", "watchlist-source" },
|
||||||
{ "f0f40559-a43a-4b8f-85ef-bdb1de1a912a", "watchlist-rss" },
|
{ "f0f40559-a43a-4b8f-85ef-bdb1de1a912a", "watchlist-rss" },
|
||||||
@ -242,9 +246,13 @@ std::unordered_set<std::string> g_ignored_guids
|
|||||||
"a548af72-b804-4d05-8569-52785952d31d", // Unknown, used in LibraryRequestHandler
|
"a548af72-b804-4d05-8569-52785952d31d", // Unknown, used in LibraryRequestHandler
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::bitset<416>* g_feature_flags;
|
||||||
|
|
||||||
SafetyHookInline _is_feature_available{};
|
SafetyHookInline _is_feature_available{};
|
||||||
SafetyHookInline _map_find{};
|
SafetyHookInline _map_find{};
|
||||||
|
SafetyHookInline _is_user_feature_set{};
|
||||||
SafetyHookInline _feature_manager_init{};
|
SafetyHookInline _feature_manager_init{};
|
||||||
|
SafetyHookInline _bitset_init{};
|
||||||
auto _feature_manager = reinterpret_cast<FeatureManager*>(0);
|
auto _feature_manager = reinterpret_cast<FeatureManager*>(0);
|
||||||
|
|
||||||
uintptr_t get_current_process_handle()
|
uintptr_t get_current_process_handle()
|
||||||
@ -369,9 +377,11 @@ bool process_feature(const char* guid, [[maybe_unused]] uintptr_t caller)
|
|||||||
return false;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
@ -379,10 +389,12 @@ uint64_t hook_is_feature_available(uintptr_t user, const char** feature)
|
|||||||
return _is_feature_available.call<uint64_t>(user, 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() &&
|
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;
|
static uint64_t FAKE_PTR = 0;
|
||||||
|
|
||||||
@ -392,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);
|
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)
|
FeatureManager* hook_feature_manager_init(FeatureManager* rcx)
|
||||||
{
|
{
|
||||||
return _feature_manager = _feature_manager_init.call<FeatureManager*>(rcx);
|
return _feature_manager = _feature_manager_init.call<FeatureManager*>(rcx);
|
||||||
@ -417,6 +448,25 @@ void hook()
|
|||||||
|
|
||||||
const auto start = std::get<0>(info.value());
|
const auto start = std::get<0>(info.value());
|
||||||
const auto end = std::get<1>(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 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 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");
|
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");
|
||||||
@ -425,7 +475,7 @@ void hook()
|
|||||||
if((!map_find && !is_feature_available) || !feature_manager_init || !feature_map_offset)
|
if((!map_find && !is_feature_available) || !feature_manager_init || !feature_map_offset)
|
||||||
{
|
{
|
||||||
#if _DEBUG
|
#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
|
#endif
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -24,10 +24,24 @@ public:
|
|||||||
member_at(uintptr_t, m_map_offset, m_feature_map);
|
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();
|
uintptr_t get_current_process_handle();
|
||||||
std::optional<std::tuple<uintptr_t, uintptr_t>> get_section_info(std::string_view name);
|
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);
|
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_is_feature_available(uintptr_t rcx, str_holder* guid);
|
||||||
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);
|
||||||
|
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);
|
FeatureManager* hook_feature_manager_init(FeatureManager* rcx);
|
||||||
void hook();
|
void hook();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user