From 370bdd087ae3bfc621ce052c98cd8dfbff413a4d Mon Sep 17 00:00:00 2001 From: andon Date: Thu, 26 Jan 2017 23:27:38 -0500 Subject: [PATCH] Framerate limiter works again --- include/DLL_VERSION.H | 2 +- include/scanner.h | 1 + src/dllmain.cpp | 16 ++- src/framerate.cpp | 101 ++++++++---------- src/render.cpp | 37 +++++-- src/scanner.cpp | 239 ++++++++++++++++++++++++++++++++++++++++-- version.ini | Bin 5362 -> 5560 bytes 7 files changed, 321 insertions(+), 75 deletions(-) diff --git a/include/DLL_VERSION.H b/include/DLL_VERSION.H index c107424..b588c42 100644 --- a/include/DLL_VERSION.H +++ b/include/DLL_VERSION.H @@ -3,7 +3,7 @@ #define TBF_MAJOR 0 #define TBF_MINOR 4 -#define TBF_BUILD 1 +#define TBF_BUILD 2 #define TBF_REV 0 diff --git a/include/scanner.h b/include/scanner.h index 9ae4d93..1958a6a 100644 --- a/include/scanner.h +++ b/include/scanner.h @@ -26,6 +26,7 @@ #include void* TBF_Scan (uint8_t* pattern, size_t len, uint8_t* mask = nullptr); +void* TBF_ScanEx (uint8_t* pattern, size_t len, uint8_t* mask, void* after); uintptr_t TBF_GetBaseAddr (void); #endif /* __TBF__SCANNER_H__ */ \ No newline at end of file diff --git a/src/dllmain.cpp b/src/dllmain.cpp index 8e9857d..fd8dcb5 100644 --- a/src/dllmain.cpp +++ b/src/dllmain.cpp @@ -129,9 +129,23 @@ SKPlugIn_Init (HMODULE hModSpecialK) //tbf::FileIO::Init (); //tbf::SteamFix::Init (); tbf::RenderFix::Init (); - tbf::FrameRateFix::Init (); //tbf::KeyboardFix::Init (); + + CreateThread ( nullptr, 0, + [](LPVOID user) -> + DWORD { + // Wait for Denuvo to finish its thing... + Sleep (15000UL); + tbf::FrameRateFix::Init (); + + CloseHandle (GetCurrentThread ()); + + return 0; + }, + nullptr, 0x00, nullptr ); + + // Uncomment this when spawning a thread //CoUninitialize (); } diff --git a/src/framerate.cpp b/src/framerate.cpp index c5b02e6..4d3fb80 100644 --- a/src/framerate.cpp +++ b/src/framerate.cpp @@ -36,7 +36,7 @@ // @TODO: Develop a heuristic to scan for this memory address; // hardcoding it is going to break stuff :) // -uintptr_t TICK_ADDR_BASE = 0xB1B074; +uintptr_t TICK_ADDR_BASE = 0xB15074;//0xB1B074; int32_t tbf::FrameRateFix::tick_scale = INT32_MAX; // 0 FPS @@ -210,73 +210,62 @@ tbf::FrameRateFix::Init (void) TBF_ApplyQueuedHooks (); - //if (config.framerate.replace_limiter) { - /* - 6D3610 0x48 0x83 0xec 0x38 ; Namco Limiter - 6D3610 0xc3 0x90 0x90 0x90 ; No Limiter + //Tales of Berseria.Kaim::Thread::`default constructor closure'+251F60 - 48 83 EC 38 - sub rsp,38 { 56 } + //Tales of Berseria.Kaim::Thread::`default constructor closure'+251F64 - 8B 0D 3A691FFF - mov ecx,["Tales of Berseria.exe"+B15074] { [1104.03] } - Tales of Berseria.exe+6D3610 - 48 83 EC 38 - sub rsp,38 { 56 } - 0x48 0x83 0xec 0x38 0x8b 0x0d -------- 0x85 0xc9 - */ + uint8_t sig [] = { 0x48, 0x83, 0xec, 0x38, 0X8B, 0x0d, 0x3A, 0x69, 0x1F, 0xFF, 0x85, 0xc9 }; + uint8_t mask [] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff }; - uint8_t sig [] = { 0x48, 0x83, 0xec, 0x38, - 0x8b, 0x0d, 0xff, 0xff, 0xff, 0xff, - 0x85, 0xc9 }; - uint8_t mask [] = { 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff }; + void* limiter_addr = + TBF_Scan (sig, sizeof (sig), mask); - void* limiter_addr = - TBF_Scan (sig, sizeof (sig), mask); - if (limiter_addr != nullptr) { - dll_log->Log ( L"[FrameLimit] Scanned Namco's Framerate Limit Bug Address: %ph", - limiter_addr ); - } else { - dll_log->Log (L"[FrameLimit] >> ERROR: Unable to find Framerate Limiter code!"); - } + if (limiter_addr != nullptr) { + dll_log->Log ( L"[FrameLimit] Scanned Namco's Framerate Limit Bug Address: %ph", + limiter_addr ); + } else { + dll_log->Log (L"[FrameLimit] >> ERROR: Unable to find Framerate Limiter code!"); + } - if (limiter_addr != nullptr) { - //dll_log->LogEx (true, L"[FrameLimit] * Installing variable rate simulation... "); + if (limiter_addr != nullptr) { + //dll_log->LogEx (true, L"[FrameLimit] * Installing variable rate simulation... "); #if 0 - uint8_t disable_inst [] = { 0xc3, 0x90, 0x90, 0x90 }; + uint8_t disable_inst [] = { 0xc3, 0x90, 0x90, 0x90 }; - TBF_InjectMachineCode ( limiter_addr, - disable_inst, - sizeof (disable_inst), - PAGE_EXECUTE_READWRITE ); + TBF_InjectMachineCode ( limiter_addr, + disable_inst, + sizeof (disable_inst), + PAGE_EXECUTE_READWRITE ); #endif - TBF_CreateFuncHook ( L"NamcoLimiter", - limiter_addr, - NamcoLimiter_Detour, - (LPVOID *)&NamcoLimiter_Original ); - TBF_EnableHook (limiter_addr); - - - MEMORY_BASIC_INFORMATION minfo; - DWORD* pTickAddr = - (DWORD *)(TBF_GetBaseAddr () + TICK_ADDR_BASE); - - VirtualQuery (pTickAddr, &minfo, sizeof (minfo)); - - if ( (minfo.Protect & PAGE_READWRITE) && - *pTickAddr <= 2 ) - { - variable_speed_installed = true; - } - - else if (config.framerate.replace_limiter) - { - SK_GetCommandProcessor ()->ProcessCommandFormatted ( - "TargetFPS %f", - 60.0f ); - } - } - //} + TBF_CreateFuncHook ( L"NamcoLimiter", + limiter_addr, + NamcoLimiter_Detour, + (LPVOID *)&NamcoLimiter_Original ); + TBF_EnableHook (limiter_addr); + + + DWORD* pTickAddr = + (DWORD *)(TBF_GetBaseAddr () + TICK_ADDR_BASE); + + __try + { + if ( *pTickAddr <= 2 ) + { + variable_speed_installed = true; + } + } + + __except ( ( GetExceptionCode () == EXCEPTION_ACCESS_VIOLATION ) ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) + { + dll_log->Log ( L"[Import Tbl] Access Violation While Attempting to " + L"Install Variable Rate Limiter." ); + } + } } void diff --git a/src/render.cpp b/src/render.cpp index 132b00a..d4580fd 100644 --- a/src/render.cpp +++ b/src/render.cpp @@ -585,22 +585,37 @@ D3D9EndScene_Detour (IDirect3DDevice9* This) = (SKX_DrawExternalOSD_pfn)GetProcAddress (hMod, "SKX_DrawExternalOSD"); - extern bool __show_cache; + static DWORD dwFirstTime = timeGetTime (); + static bool show_disclaimer = true; - if (__show_cache) { - static std::string output; + if (show_disclaimer) + { + if (timeGetTime() > dwFirstTime + 10000UL) + show_disclaimer = false; + + SKX_DrawExternalOSD ("ToBFix", "Press Ctrl + Shift + O to toggle In-Game OSD\n" + "Press Ctrl + Shift + Backspace to access In-Game Config Menu"); + } - output = "Texture Cache\n"; - output += "-------------\n"; - output += tbf::RenderFix::tex_mgr.osdStats (); + else + { + extern bool __show_cache; - output += mod_text; + if (__show_cache) { + static std::string output; - SKX_DrawExternalOSD ("ToBFix", output.c_str ()); + output = "Texture Cache\n"; + output += "-------------\n"; + output += tbf::RenderFix::tex_mgr.osdStats (); - output = ""; - } else - SKX_DrawExternalOSD ("ToBFix", mod_text.c_str ()); + output += mod_text; + + SKX_DrawExternalOSD ("ToBFix", output.c_str ()); + + output = ""; + } else + SKX_DrawExternalOSD ("ToBFix", mod_text.c_str ()); + } mod_text = ""; diff --git a/src/scanner.cpp b/src/scanner.cpp index 98c7c81..c1d5ead 100644 --- a/src/scanner.cpp +++ b/src/scanner.cpp @@ -21,15 +21,17 @@ **/ #include "scanner.h" +#include #include +static LPVOID __SK_base_img_addr = nullptr; +static LPVOID __SK_end_img_addr = nullptr; + uintptr_t TBF_GetBaseAddr (void) { - static uintptr_t base = (uintptr_t)GetModuleHandle (nullptr); - - return base; + return (uintptr_t)__SK_base_img_addr; } void* @@ -40,12 +42,66 @@ TBF_Scan (uint8_t* pattern, size_t len, uint8_t* mask) MEMORY_BASIC_INFORMATION mem_info; VirtualQuery (base_addr, &mem_info, sizeof mem_info); + // + // VMProtect kills this, so let's do something else... + // +#ifdef VMPROTECT_IS_NOT_A_FACTOR IMAGE_DOS_HEADER* pDOS = (IMAGE_DOS_HEADER *)mem_info.AllocationBase; IMAGE_NT_HEADERS* pNT = (IMAGE_NT_HEADERS *)((uintptr_t)(pDOS + pDOS->e_lfanew)); uint8_t* end_addr = base_addr + pNT->OptionalHeader.SizeOfImage; +#else + base_addr = (uint8_t *)mem_info.BaseAddress;//AllocationBase; + uint8_t* end_addr = (uint8_t *)mem_info.BaseAddress + mem_info.RegionSize; + + if (base_addr != (uint8_t *)0x400000) { + dll_log->Log ( L"[ Sig Scan ] Expected module base addr. 40000h, but got: %ph", + base_addr ); + } + + size_t pages = 0; + +// Scan up to 256 MiB worth of data +#ifdef __WIN32 +uint8_t* const PAGE_WALK_LIMIT = (base_addr + (uintptr_t)(1ULL << 26)); +#else + // Dark Souls 3 needs this, its address space is completely random to the point + // where it may be occupying a range well in excess of 36 bits. Probably a stupid + // anti-cheat attempt. +uint8_t* const PAGE_WALK_LIMIT = (base_addr + (uintptr_t)(1ULL << 36)); +#endif + + // + // For practical purposes, let's just assume that all valid games have less than 256 MiB of + // committed executable image data. + // + while (VirtualQuery (end_addr, &mem_info, sizeof mem_info) && end_addr < PAGE_WALK_LIMIT) { + //if (mem_info.Protect & PAGE_NOACCESS || (! (mem_info.Type & MEM_IMAGE))) + //break; + + pages += VirtualQuery (end_addr, &mem_info, sizeof mem_info); + + end_addr = (uint8_t *)mem_info.BaseAddress + mem_info.RegionSize; + } + + if (end_addr > PAGE_WALK_LIMIT) { + dll_log->Log ( L"[ Sig Scan ] Module page walk resulted in end addr. out-of-range: %ph", + end_addr ); + dll_log->Log ( L"[ Sig Scan ] >> Restricting to %ph", + PAGE_WALK_LIMIT ); + end_addr = (uint8_t *)PAGE_WALK_LIMIT; + } + + dll_log->Log ( L"[ Sig Scan ] Module image consists of %zu pages, from %ph to %ph", + pages, + base_addr, + end_addr ); +#endif + + __SK_base_img_addr = base_addr; + __SK_end_img_addr = end_addr; uint8_t* begin = (uint8_t *)base_addr; uint8_t* it = begin; @@ -55,15 +111,27 @@ TBF_Scan (uint8_t* pattern, size_t len, uint8_t* mask) { VirtualQuery (it, &mem_info, sizeof mem_info); - uint8_t* next_rgn = (uint8_t *)mem_info.BaseAddress + mem_info.RegionSize; + // Bail-out once we walk into an address range that is not resident, because + // it does not belong to the original executable. + if (mem_info.RegionSize == 0) + break; + + uint8_t* next_rgn = + (uint8_t *)mem_info.BaseAddress + mem_info.RegionSize; - if (mem_info.Type != MEM_IMAGE || mem_info.State != MEM_COMMIT || mem_info.Protect & PAGE_NOACCESS) { + if ( (! (mem_info.Type & MEM_IMAGE)) || + (! (mem_info.State & MEM_COMMIT)) || + mem_info.Protect & PAGE_NOACCESS ) { it = next_rgn; idx = 0; begin = it; continue; } + // Do not search past the end of the module image! + if (next_rgn >= end_addr) + break; + while (it < next_rgn) { uint8_t* scan_addr = it; @@ -86,7 +154,166 @@ TBF_Scan (uint8_t* pattern, size_t len, uint8_t* mask) if (it > end_addr - len) break; - + it = ++begin; + idx = 0; + } + } + } + + return nullptr; +} + +BOOL +SK_InjectMemory ( LPVOID base_addr, + uint8_t* new_data, + size_t data_size, + DWORD permissions, + uint8_t* old_data = nullptr + ) +{ + DWORD dwOld; + + if (VirtualProtect (base_addr, data_size, permissions, &dwOld)) + { + if (old_data != nullptr) + memcpy (old_data, base_addr, data_size); + + memcpy (base_addr, new_data, data_size); + + VirtualProtect (base_addr, data_size, dwOld, &dwOld); + + return TRUE; + } + + return FALSE; +} + +void* +TBF_ScanEx (uint8_t* pattern, size_t len, uint8_t* mask, void* after) +{ + uint8_t* base_addr = (uint8_t *)GetModuleHandle (nullptr); + + MEMORY_BASIC_INFORMATION mem_info; + VirtualQuery (base_addr, &mem_info, sizeof mem_info); + + // + // VMProtect kills this, so let's do something else... + // +#ifdef VMPROTECT_IS_NOT_A_FACTOR + IMAGE_DOS_HEADER* pDOS = + (IMAGE_DOS_HEADER *)mem_info.AllocationBase; + IMAGE_NT_HEADERS* pNT = + (IMAGE_NT_HEADERS *)((uintptr_t)(pDOS + pDOS->e_lfanew)); + + uint8_t* end_addr = base_addr + pNT->OptionalHeader.SizeOfImage; +#else + base_addr = (uint8_t *)mem_info.BaseAddress;//AllocationBase; + uint8_t* end_addr = (uint8_t *)mem_info.BaseAddress + mem_info.RegionSize; + + if (base_addr != (uint8_t *)0x400000) { + dll_log->Log ( L"[ Sig Scan ] Expected module base addr. 40000h, but got: %ph", + base_addr ); + } + + size_t pages = 0; + +// Scan up to 256 MiB worth of data +#ifdef __WIN32 +uint8_t* const PAGE_WALK_LIMIT = (base_addr + (uintptr_t)(1ULL << 26)); +#else + // Dark Souls 3 needs this, its address space is completely random to the point + // where it may be occupying a range well in excess of 36 bits. Probably a stupid + // anti-cheat attempt. +uint8_t* const PAGE_WALK_LIMIT = (base_addr + (uintptr_t)(1ULL << 36)); +#endif + + // + // For practical purposes, let's just assume that all valid games have less than 256 MiB of + // committed executable image data. + // + while (VirtualQuery (end_addr, &mem_info, sizeof mem_info) && end_addr < PAGE_WALK_LIMIT) { + //if (mem_info.Protect & PAGE_NOACCESS || (! (mem_info.Type & MEM_IMAGE))) + //break; + + pages += VirtualQuery (end_addr, &mem_info, sizeof mem_info); + + end_addr = (uint8_t *)mem_info.BaseAddress + mem_info.RegionSize; + } + + if (end_addr > PAGE_WALK_LIMIT) { + dll_log->Log ( L"[ Sig Scan ] Module page walk resulted in end addr. out-of-range: %ph", + end_addr ); + dll_log->Log ( L"[ Sig Scan ] >> Restricting to %ph", + PAGE_WALK_LIMIT ); + end_addr = (uint8_t *)PAGE_WALK_LIMIT; + } + + dll_log->Log ( L"[ Sig Scan ] Module image consists of %zu pages, from %ph to %ph", + pages, + base_addr, + end_addr ); +#endif + + __SK_base_img_addr = base_addr; + __SK_end_img_addr = end_addr; + + uint8_t* begin = (uint8_t *)base_addr; + uint8_t* it = begin; + int idx = 0; + + while (it < end_addr) + { + VirtualQuery (it, &mem_info, sizeof mem_info); + + // Bail-out once we walk into an address range that is not resident, because + // it does not belong to the original executable. + if (mem_info.RegionSize == 0) + break; + + uint8_t* next_rgn = + (uint8_t *)mem_info.BaseAddress + mem_info.RegionSize; + + if ( (! (mem_info.Type & MEM_IMAGE)) || + (! (mem_info.State & MEM_COMMIT)) || + mem_info.Protect & PAGE_NOACCESS ) { + it = next_rgn; + idx = 0; + begin = it; + continue; + } + + // Do not search past the end of the module image! + if (next_rgn >= end_addr) + break; + + while (it < next_rgn) { + uint8_t* scan_addr = it; + + bool match = (*scan_addr == pattern [idx]); + + // For portions we do not care about... treat them + // as matching. + if (mask != nullptr && (! mask [idx])) + match = true; + + if (match) { + if (++idx == len) { + if ((void *)begin > after) + return (void *)begin; + else { + it = ++begin; + idx = 0; + continue; + } + } + + ++it; + } + + else { + // No match?! + if (it > end_addr - len) + break; it = ++begin; idx = 0; diff --git a/version.ini b/version.ini index a092ff03cafb0ca0b34af2498779fe36e3dc4583..00058d38f94eb548a510329a8b645eba1475e5df 100644 GIT binary patch delta 260 zcmeyQxkGzG0i)5x!kLjl45|od(vYz~BND&toWMC}YUq{FRxRadH}y1-lW04uc_s`Q)Wc yl^{E{Cwnmm(b!%_qshJO#*=kexfsnio3kEfnk>ZK#B9P~H2ETz?dEUne|P{0P&y|7 delta 246 zcmdm?{Yi5|0i)r>!kLkd3@Hp*45bXk3?&S?45seEI5~~UVzL1%8>89erA(C| k8?`5UF$dAuRz}0gz3j%5by&F=%{H5}9%kBnfa3=b01^u~^Z)<=