mirror of
https://gitgud.io/yuv420p10le/plexmediaserver_crack
synced 2025-07-05 08:54:44 +00:00
Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1906022184 | ||
![]() |
a3001d1550 | ||
![]() |
1209483a5b | ||
![]() |
36046155d1 | ||
![]() |
7d5a79c019 | ||
![]() |
d4f1d18bac |
@ -123,7 +123,7 @@ docker run --rm -v plexmediaserver_crack/linux:/src:rw glibc bash -c "mkdir -p b
|
||||
|
||||
* 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
|
||||
|
||||
|
Binary file not shown.
Binary file not shown.
@ -221,6 +221,7 @@ 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()
|
||||
{
|
||||
@ -388,6 +389,11 @@ uint64_t hook_bitset_init(uintptr_t rcx)
|
||||
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();
|
||||
@ -400,11 +406,19 @@ 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 + sizeof(uintptr_t));
|
||||
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)
|
||||
{
|
||||
|
@ -12,4 +12,5 @@ uintptr_t follow_call_rel32(const uintptr_t address);
|
||||
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();
|
||||
|
@ -51,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" },
|
||||
@ -138,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" },
|
||||
@ -167,7 +167,6 @@ 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" },
|
||||
@ -251,6 +250,7 @@ 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);
|
||||
@ -377,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;
|
||||
}
|
||||
@ -387,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;
|
||||
|
||||
@ -406,10 +410,19 @@ void hook_bitset_init(uintptr_t rcx, uintptr_t rdx)
|
||||
g_feature_flags->set();
|
||||
|
||||
#if _DEBUG
|
||||
std::println("[INFO] [plexmediaserver_crack] Forced feature flags on.", reinterpret_cast<uintptr_t>(g_feature_flags));
|
||||
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);
|
||||
@ -436,12 +449,18 @@ 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 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() + 6 + *reinterpret_cast<uint32_t*>(bitset.value() + 9);
|
||||
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));
|
||||
|
||||
|
@ -24,11 +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