1
0
Fork 0
mirror of https://github.com/Detanup01/gbe_fork.git synced 2025-08-06 23:55:42 +02:00

Merge pull request #295 from otavepto/patch/stub

Support more stub variants
This commit is contained in:
Detanup01 2025-07-25 10:17:36 +02:00 committed by GitHub
commit 8dc06a0144
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 119 additions and 82 deletions

View file

@ -9,10 +9,6 @@
#include <thread>
#include <string>
#if defined(DEBUG) || defined(_DEBUG)
#define STUB_EXTRA_DEBUG
#endif
static std::mutex dll_unload_mtx{};
static std::condition_variable dll_unload_cv{};
static bool unload_dll = false;
@ -20,6 +16,14 @@ static bool unload_dll = false;
static HMODULE my_hModule = nullptr;
static HANDLE unload_thread_handle = INVALID_HANDLE_VALUE;
constexpr const auto UNLOAD_TIMEOUT =
#ifdef STUB_EXTRA_DEBUG
std::chrono::minutes(5)
#else
std::chrono::seconds(5)
#endif
;
static void send_unload_signal()
{
@ -32,14 +36,6 @@ static void send_unload_signal()
DWORD WINAPI self_unload(LPVOID lpParameter)
{
constexpr const auto UNLOAD_TIMEOUT =
#ifdef STUB_EXTRA_DEBUG
std::chrono::minutes(5)
#else
std::chrono::seconds(5)
#endif
;
{
#ifdef STUB_EXTRA_DEBUG
auto t1 = std::chrono::high_resolution_clock::now();
@ -61,14 +57,10 @@ DWORD WINAPI self_unload(LPVOID lpParameter)
FreeLibraryAndExitThread(my_hModule, 0);
}
BOOL APIENTRY DllMain(
HMODULE hModule,
DWORD reason,
LPVOID lpReserved)
BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID lpReserved)
{
switch (reason)
{
case DLL_PROCESS_ATTACH:
switch (reason) {
case DLL_PROCESS_ATTACH: {
stubdrm::set_cleanup_cb(send_unload_signal);
my_hModule = hModule;
if (!stubdrm::patch()) {
@ -82,12 +74,13 @@ BOOL APIENTRY DllMain(
return FALSE;
}
unload_thread_handle = CreateThread(nullptr, 0, self_unload, nullptr, 0, nullptr);
break;
}
break;
case DLL_PROCESS_DETACH:
case DLL_PROCESS_DETACH: {
if (!unload_dll) { // not unloaded yet, just an early exit, or thread timed out
#ifdef STUB_EXTRA_DEBUG
MessageBoxA(nullptr, "Unclean exit", "Main", MB_OK | MB_ICONERROR);
MessageBoxA(nullptr, "Unclean exit; dll unloaded or thread timed out)", "Main", MB_OK | MB_ICONERROR);
#endif
stubdrm::restore();
@ -95,7 +88,8 @@ BOOL APIENTRY DllMain(
TerminateThread(unload_thread_handle, 0);
}
}
break;
}
break;
}
return TRUE;

View file

@ -1,7 +1,12 @@
#pragma once
namespace stubdrm
{
#if !defined(STUB_EXTRA_DEBUG)
#if defined(DEBUG) || defined(_DEBUG)
#define STUB_EXTRA_DEBUG
#endif
#endif
namespace stubdrm {
bool patch();
bool restore();

View file

@ -8,6 +8,9 @@
#include <mutex>
#include <intrin.h>
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
// MinGW doesn't implement _AddressOfReturnAddress(), throws linker error
// https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html
// https://learn.microsoft.com/en-us/cpp/intrinsics/addressofreturnaddress
@ -69,7 +72,7 @@ static const std::vector<BindSnrDetails_t> all_bind_details {
{
// bind_detection_patts[]
{
"FF D? 44 0F B6 ?? 3C 30 0F 85", // appid: 537450 (rare, only found in this appid!)
"FF D? 44 0F B6 ?? 3C 30 0F 85", // appid: 537450, 427820
},
// stub_details[]
{
@ -86,10 +89,14 @@ static const std::vector<BindSnrDetails_t> all_bind_details {
"E8 ?? ?? ?? ?? 84 C0 75 ?? B0 3?",
"B8 01 00 00 00 ?? ?? EB",
},
{
{ // 537450
"E8 ?? ?? ?? ?? 44 0F B6 ?? 3C 30 0F 84 ?? ?? ?? ?? 3C",
"B8 30 00 00 00 ?? ?? ?? ?? ?? ?? 90 E9",
},
{ // 427820
"E8 ?? ?? ?? ?? 0F B6 ?? 3C 30 7? ?? 3C",
"B8 30 00 00 00 ?? ?? ?? ?? ?? EB",
},
},
},
},
@ -135,6 +142,34 @@ static const std::vector<BindSnrDetails_t> all_bind_details {
},
// stub_details[]
{
{ // appid 250180
// stub_detection_patt
"3? ?? ?? 7? ?? 6? ?? E9 ?? ?? ?? ?? 68 ?? ?? ?? ?? 5? 8? ?? ?? 5? 5?",
// change memory pages access to r/w/e
true, // appid 48000
// stub_snr_units[]
{
{
"F6 05 ?? ?? ?? ?? 02 89 ?? ?? 0F 85 ?? ?? ?? ?? 5? FF ?? 6?",
"?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 90 E9 03 03 00 00",
},
},
},
{ // appid 257220
// stub_detection_patt
"3? ?? ?? 7? ?? 6? ?? E9 ?? ?? ?? ?? 8? ?? ?? 5? 5?",
// change memory pages access to r/w/e
true, // appid 48000
// stub_snr_units[]
{
{
"F6 05 ?? ?? ?? ?? 02 89 ?? ?? 0F 85 ?? ?? ?? ?? 5? FF ?? 6?",
"?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 90 E9 FC 02 00 00",
},
},
},
{
// stub_detection_patt
"??",
@ -150,10 +185,6 @@ static const std::vector<BindSnrDetails_t> all_bind_details {
"F6 C? 02 89 ?? ?? ?? ?? ?? A3 ?? ?? ?? ?? 0F 85",
"?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 90 E9 00 03 00 00",
},
{ // appid 250180
"F6 05 ?? ?? ?? ?? 02 89 ?? ?? 0F 85 ?? ?? ?? ?? 5? FF ?? 6?",
"?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 90 E9 03 03",
},
},
},
},
@ -182,6 +213,10 @@ static const std::vector<BindSnrDetails_t> all_bind_details {
"5? E8 ?? ?? ?? ?? 8? ?? 83 C4 ?? 8? F? 30 74 ?? 8?",
"?? B8 30 00 00 00 ?? ?? ?? ?? ?? ?? ?? ?? EB",
},
{ // appid 250320
"5? E8 ?? ?? ?? ?? 83 C4 ?? 88 45 ?? 3C 30 74",
"?? B8 30 00 00 00 ?? ?? ?? ?? ?? ?? ?? ?? EB",
},
},
},
},
@ -219,7 +254,7 @@ static const std::vector<BindSnrDetails_t> all_bind_details {
"F6 C? 02 89 ?? ?? ?? ?? ?? A3 ?? ?? ?? ?? 0F 85",
"?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 90 E9 2A 02 00 00",
},
{
{ // 94530, 94590
"6A 04 5? 5? 8D",
"?? ?? ?? E9 BF 00 00 00",
},
@ -255,29 +290,34 @@ static void change_mem_pages_access()
auto sections = pe_helpers::get_section_headers((HMODULE)exe_addr_base);
if (!sections.count) return;
constexpr const static unsigned ANY_EXECUTE_RIGHT = PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY;
constexpr const static unsigned PAGE_ANY_EXECUTE_RIGHT = PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY;
for (size_t i = 0; i < sections.count; ++i) {
auto section = sections.ptr[i];
uint8_t *section_base_addr = exe_addr_base + section.VirtualAddress;
MEMORY_BASIC_INFORMATION mbi{};
if (VirtualQuery((LPCVOID)section_base_addr, &mbi, sizeof(mbi)) && // function succeeded
(mbi.Protect & ANY_EXECUTE_RIGHT)) { // this page (not entire section) has execute rights
(mbi.Protect & PAGE_ANY_EXECUTE_RIGHT)) { // this page (not entire section) has execute rights
DWORD current_protection = 0;
auto res = VirtualProtect(section_base_addr, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &current_protection);
// if (!res) {
// MessageBoxA(
// nullptr,
// (std::string("Failed to change access of page '") + (char *)section.Name + "' ").c_str(),
// "Failed",
// MB_OK
// );
// }
#ifdef STUB_EXTRA_DEBUG
if (!res) {
std::string msg = std::string("Failed to change access of mem page '") + (char *)section.Name + "' @" + std::to_string((unsigned long long)section_base_addr);
MessageBoxA(nullptr, msg.c_str(), "Mem pages access change failed", MB_OK | MB_ICONERROR);
}
#endif
}
}
}
extern "C" __declspec(dllexport) void __cdecl patch_done()
{
#ifdef STUB_EXTRA_DEBUG
MessageBoxA(nullptr, "You can hook a debugger here!", "Patch done", MB_OK | MB_ICONASTERISK);
#endif
}
static void call_cleanup_cb()
{
if (cleanup_cb) {
@ -293,55 +333,59 @@ static void patch_if_possible(void *ret_addr)
auto page_details = pe_helpers::get_mem_page_details(ret_addr);
if (!page_details.BaseAddress || page_details.AllocationProtect != PAGE_READWRITE) return;
// find stub variant
const StubSnrDetails_t *current_stub = nullptr;
bool anything_found = false;
bool change_mem_access = false;
const auto &bind_details = all_bind_details[current_bind_idx];
for (const auto &stub_details : bind_details.stub_details) {
auto mem = pe_helpers::search_memory(
(uint8_t *)page_details.BaseAddress,
// find stub variant
auto stub_variant_mem = pe_helpers::search_memory(
reinterpret_cast<uint8_t *>(page_details.BaseAddress),
page_details.RegionSize,
stub_details.stub_detection_patt);
if (mem) {
current_stub = &stub_details;
break;
if (!stub_variant_mem) {
continue;
}
}
if (!current_stub) {
// we can't remove hooks here, the drm allocates many pages with read/write access to decrypt other parts
// and their code also gets here, if we restore hooks then we can't patch the actual stub page (which comes much later)
return;
}
// patch all snr units inside stub
bool anything_found = false;
for (const auto &snr_unit : current_stub->stub_snr_units) {
auto mem = pe_helpers::search_memory(
(uint8_t *)page_details.BaseAddress,
page_details.RegionSize,
snr_unit.search_patt);
if (mem) {
anything_found = true;
// patch all snr units inside stub
for (const auto &snr_unit : stub_details.stub_snr_units) {
auto search_patt_mem = pe_helpers::search_memory(
reinterpret_cast<uint8_t *>(page_details.BaseAddress),
page_details.RegionSize,
snr_unit.search_patt);
auto size_until_match = mem - (uint8_t *)page_details.BaseAddress;
bool ok = pe_helpers::replace_memory(
mem,
page_details.RegionSize - size_until_match,
snr_unit.replace_patt,
GetCurrentProcess());
// if (!ok) return;
if (search_patt_mem) {
anything_found = true;
change_mem_access |= stub_details.change_mem_access;
auto size_before_match = search_patt_mem - (uint8_t *)page_details.BaseAddress;
bool ok = pe_helpers::replace_memory(
search_patt_mem,
page_details.RegionSize - size_before_match,
snr_unit.replace_patt,
GetCurrentProcess());
#ifdef STUB_EXTRA_DEBUG
if (!ok) {
std::string msg = std::string("Failed to patch pattern @") + std::to_string((unsigned long long)search_patt_mem) + " >> '" + snr_unit.search_patt + "'";
MessageBoxA(nullptr, msg.c_str(), "SnR patch failed", MB_OK | MB_ICONERROR);
}
#endif
}
}
}
if (anything_found) {
// MessageBoxA(NULL, ("ret addr = " + std::to_string((size_t)ret_addr)).c_str(), "Patched", MB_OK);
restore_win32_apis();
if (current_stub->change_mem_access) {
if (change_mem_access) {
change_mem_pages_access();
}
patch_done();
call_cleanup_cb();
} else {
// we can't remove hooks here, the drm allocates many pages with read/write access to decrypt other parts
// and their code also gets here, if we restore hooks then we can't patch the actual stub page (which executes much later)
}
}
@ -363,9 +407,7 @@ static DWORD WINAPI GetTickCount_hook()
static decltype(GetModuleHandleA) *actual_GetModuleHandleA = GetModuleHandleA;
__declspec(noinline)
static HMODULE WINAPI GetModuleHandleA_hook(
LPCSTR lpModuleName
)
static HMODULE WINAPI GetModuleHandleA_hook(LPCSTR lpModuleName)
{
if (GetModuleHandleA_hooked &&
lpModuleName && lpModuleName[0] &&
@ -382,11 +424,7 @@ static HMODULE WINAPI GetModuleHandleA_hook(
static decltype(GetModuleHandleExA) *actual_GetModuleHandleExA = GetModuleHandleExA;
__declspec(noinline)
static BOOL WINAPI GetModuleHandleExA_hook(
DWORD dwFlags,
LPCSTR lpModuleName,
HMODULE *phModule
)
static BOOL WINAPI GetModuleHandleExA_hook(DWORD dwFlags, LPCSTR lpModuleName, HMODULE *phModule)
{
constexpr const static unsigned HANDLE_FROM_ADDR = GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT;
if (GetModuleHandleExA_hooked &&