mirror of
https://github.com/Detanup01/gbe_fork.git
synced 2025-01-13 02:43:54 +08:00
Merge pull request #118 from otavepto/patch/detect-broken-bind
fix detection of broken bind
This commit is contained in:
commit
09282941f8
@ -7,7 +7,11 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#if defined(DEBUG) || defined(_DEBUG)
|
||||||
|
#define STUB_EXTRA_DEBUG
|
||||||
|
#endif
|
||||||
|
|
||||||
static std::mutex dll_unload_mtx{};
|
static std::mutex dll_unload_mtx{};
|
||||||
static std::condition_variable dll_unload_cv{};
|
static std::condition_variable dll_unload_cv{};
|
||||||
@ -29,7 +33,7 @@ static void send_unload_signal()
|
|||||||
DWORD WINAPI self_unload(LPVOID lpParameter)
|
DWORD WINAPI self_unload(LPVOID lpParameter)
|
||||||
{
|
{
|
||||||
constexpr const auto UNLOAD_TIMEOUT =
|
constexpr const auto UNLOAD_TIMEOUT =
|
||||||
#ifdef _DEBUG
|
#ifdef STUB_EXTRA_DEBUG
|
||||||
std::chrono::minutes(5)
|
std::chrono::minutes(5)
|
||||||
#else
|
#else
|
||||||
std::chrono::seconds(5)
|
std::chrono::seconds(5)
|
||||||
@ -37,8 +41,21 @@ DWORD WINAPI self_unload(LPVOID lpParameter)
|
|||||||
;
|
;
|
||||||
|
|
||||||
{
|
{
|
||||||
|
#ifdef STUB_EXTRA_DEBUG
|
||||||
|
auto t1 = std::chrono::high_resolution_clock::now();
|
||||||
|
#endif
|
||||||
|
|
||||||
std::unique_lock lock(dll_unload_mtx);
|
std::unique_lock lock(dll_unload_mtx);
|
||||||
dll_unload_cv.wait_for(lock, UNLOAD_TIMEOUT, [](){ return unload_dll; });
|
dll_unload_cv.wait_for(lock, UNLOAD_TIMEOUT, []{ return unload_dll; });
|
||||||
|
|
||||||
|
#ifdef STUB_EXTRA_DEBUG
|
||||||
|
if (!unload_dll) { // flag was not raised, means we timed out
|
||||||
|
auto t2 = std::chrono::high_resolution_clock::now();
|
||||||
|
auto dd = std::chrono::duration_cast<std::chrono::seconds>(t2 - t1);
|
||||||
|
std::string msg = "Unloading after " + std::to_string(dd.count()) + " seconds, due to timeout";
|
||||||
|
MessageBoxA(nullptr, msg.c_str(), "Self-unload thread", MB_OK | MB_ICONERROR);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
unload_thread_handle = INVALID_HANDLE_VALUE;
|
unload_thread_handle = INVALID_HANDLE_VALUE;
|
||||||
FreeLibraryAndExitThread(my_hModule, 0);
|
FreeLibraryAndExitThread(my_hModule, 0);
|
||||||
@ -52,19 +69,27 @@ BOOL APIENTRY DllMain(
|
|||||||
switch (reason)
|
switch (reason)
|
||||||
{
|
{
|
||||||
case DLL_PROCESS_ATTACH:
|
case DLL_PROCESS_ATTACH:
|
||||||
|
stubdrm::set_cleanup_cb(send_unload_signal);
|
||||||
|
my_hModule = hModule;
|
||||||
if (!stubdrm::patch()) {
|
if (!stubdrm::patch()) {
|
||||||
|
#ifdef STUB_EXTRA_DEBUG
|
||||||
|
MessageBoxA(nullptr, "Failed to detect .bind", "Main", MB_OK | MB_ICONERROR);
|
||||||
|
#endif
|
||||||
|
|
||||||
// https://learn.microsoft.com/en-us/windows/win32/dlls/dllmain
|
// https://learn.microsoft.com/en-us/windows/win32/dlls/dllmain
|
||||||
// "The system immediately calls your entry-point function with DLL_PROCESS_DETACH and unloads the DLL"
|
// "The system immediately calls your entry-point function with DLL_PROCESS_DETACH and unloads the DLL"
|
||||||
unload_dll = true;
|
unload_dll = true;
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
my_hModule = hModule;
|
|
||||||
stubdrm::set_cleanup_cb(send_unload_signal);
|
|
||||||
unload_thread_handle = CreateThread(nullptr, 0, self_unload, nullptr, 0, nullptr);
|
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
|
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);
|
||||||
|
#endif
|
||||||
|
|
||||||
stubdrm::restore();
|
stubdrm::restore();
|
||||||
if (unload_thread_handle != INVALID_HANDLE_VALUE && unload_thread_handle != NULL) {
|
if (unload_thread_handle != INVALID_HANDLE_VALUE && unload_thread_handle != NULL) {
|
||||||
TerminateThread(unload_thread_handle, 0);
|
TerminateThread(unload_thread_handle, 0);
|
||||||
|
@ -436,60 +436,79 @@ static bool restore_win32_apis()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static std::vector<uint8_t> get_pe_header_disk()
|
|
||||||
{
|
|
||||||
const std::string filepath = pe_helpers::get_current_exe_path() + pe_helpers::get_current_exe_name();
|
|
||||||
try {
|
|
||||||
std::ifstream file(std::filesystem::u8path(filepath), std::ios::in | std::ios::binary);
|
|
||||||
if (!file) return {};
|
|
||||||
|
|
||||||
// 2MB is enough
|
|
||||||
std::vector<uint8_t> data(2 * 1024 * 1024, 0);
|
|
||||||
file.read((char *)&data[0], data.size());
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
return data;
|
|
||||||
} catch(...) { }
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool calc_bind_section_boundaries()
|
static bool calc_bind_section_boundaries()
|
||||||
{
|
{
|
||||||
auto bind_section = pe_helpers::get_section_header_with_name(((HMODULE)exe_addr_base), ".bind");
|
constexpr static auto calc_bind_section_boundaries_from_mem = [] {
|
||||||
if (bind_section) {
|
auto bind_section = pe_helpers::get_section_header_with_name(reinterpret_cast<HMODULE>(exe_addr_base), ".bind");
|
||||||
bind_addr_base = exe_addr_base + bind_section->VirtualAddress;
|
if (!bind_section || !bind_section->VirtualAddress) return false;
|
||||||
|
|
||||||
|
uint8_t * const bind_start = exe_addr_base + bind_section->VirtualAddress;
|
||||||
|
uint8_t *bind_end = nullptr;
|
||||||
|
|
||||||
MEMORY_BASIC_INFORMATION mbi{};
|
MEMORY_BASIC_INFORMATION mbi{};
|
||||||
if (VirtualQuery((LPVOID)bind_addr_base, &mbi, sizeof(mbi)) && mbi.RegionSize > 0) {
|
if (VirtualQuery((LPVOID)bind_start, &mbi, sizeof(mbi)) && mbi.RegionSize > 0) {
|
||||||
bind_addr_end = bind_addr_base + mbi.RegionSize;
|
bind_end = bind_start + mbi.RegionSize;
|
||||||
} else if (bind_section->Misc.VirtualSize > 0) {
|
} else if (bind_section->Misc.VirtualSize > 0) {
|
||||||
bind_addr_end = bind_addr_base + bind_section->Misc.VirtualSize;
|
bind_end = bind_start + bind_section->Misc.VirtualSize;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bind_addr_base = bind_start;
|
||||||
|
bind_addr_end = bind_end;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr static auto calc_bind_section_boundaries_from_disk = [] {
|
||||||
|
constexpr static auto get_pe_header_from_disk = []() -> std::vector<uint8_t> {
|
||||||
|
try {
|
||||||
|
const std::string filepath = pe_helpers::get_current_exe_path() + pe_helpers::get_current_exe_name();
|
||||||
|
std::ifstream file(std::filesystem::u8path(filepath), std::ios::in | std::ios::binary);
|
||||||
|
if (!file) return {};
|
||||||
|
|
||||||
|
// 2MB is enough
|
||||||
|
std::vector<uint8_t> data(2 * 1024 * 1024, 0);
|
||||||
|
file.read((char *)&data[0], data.size());
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
} catch(...) { }
|
||||||
|
|
||||||
|
return {};
|
||||||
|
};
|
||||||
|
|
||||||
|
auto disk_header = get_pe_header_from_disk();
|
||||||
|
if (disk_header.empty()) return false;
|
||||||
|
|
||||||
|
auto bind_section = pe_helpers::get_section_header_with_name(reinterpret_cast<HMODULE>(&disk_header[0]), ".bind");
|
||||||
|
if (!bind_section || !bind_section->VirtualAddress) return false;
|
||||||
|
if (!bind_section->Misc.VirtualSize) return false;
|
||||||
|
|
||||||
|
bind_addr_base = exe_addr_base + bind_section->VirtualAddress;
|
||||||
|
bind_addr_end = bind_addr_base + bind_section->Misc.VirtualSize;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// appid 2677660 (build 16659541) changes the PIMAGE_OPTIONAL_HEADER->SizeOfHeaders to a size less than the actual,
|
||||||
|
// so that the ".bind" section *looks* as if it exists in memory (IMAGE_SECTION_HEADER->Name is valid), but its data is 0/nulled
|
||||||
|
if (calc_bind_section_boundaries_from_mem()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// we don't *seem* to have .bind section *in memory*
|
// otherwise we *seem* to be missing the .bind section *in memory*, but not necessarily
|
||||||
// appid 1732190 changes the PIMAGE_OPTIONAL_HEADER->SizeOfHeaders to a size less than the actual,
|
// appid 1732190 also changes the PIMAGE_OPTIONAL_HEADER->SizeOfHeaders to a size less than the actual
|
||||||
// subtracting the size of the last section, i.e ".bind" section (original size = 0x600 >>> decreased to 0x400)
|
// by subtracting the size of the last section, i.e ".bind" section (original size = 0x600 >>> decreased to 0x400)
|
||||||
// that way whenever the .exe is loaded in memory, the Windows loader will ignore populating the PE header with the info
|
// that way whenever the .exe is loaded in memory, the Windows loader will ignore populating the PE header with the info
|
||||||
// of that section *in memory* since it is not taken into consideration, but the PE header *on disk* still contains the info
|
// of that section *in memory* since it is not taken into consideration, but the PE header *on disk* still contains the info
|
||||||
//
|
//
|
||||||
// also the PIMAGE_FILE_HEADER->NumberOfSections is kept intact, otherwise the PIMAGE_OPTIONAL_HEADER->AddressOfEntryPoint
|
// also the PIMAGE_FILE_HEADER->NumberOfSections is kept intact, otherwise the PIMAGE_OPTIONAL_HEADER->AddressOfEntryPoint
|
||||||
// would be pointing at a non-existent section and the .exe won't work
|
// would be pointing at a non-existent section and the .exe won't work
|
||||||
auto disk_header = get_pe_header_disk();
|
if (calc_bind_section_boundaries_from_disk()) {
|
||||||
if (disk_header.empty()) return false;
|
return true;
|
||||||
|
}
|
||||||
bind_section = pe_helpers::get_section_header_with_name(((HMODULE)&disk_header[0]), ".bind");
|
|
||||||
if (!bind_section) return false;
|
return false;
|
||||||
|
|
||||||
bind_addr_base = exe_addr_base + bind_section->VirtualAddress;
|
|
||||||
if (!bind_section->Misc.VirtualSize) return false;
|
|
||||||
|
|
||||||
bind_addr_end = bind_addr_base + bind_section->Misc.VirtualSize;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user