mirror of
https://github.com/wangdage12/genshin-fps-unlock.git
synced 2026-02-17 09:22:08 +08:00
726 lines
26 KiB
C++
726 lines
26 KiB
C++
#define KEY_TOGGLE VK_END
|
||
#define KEY_INCREASE VK_UP
|
||
#define KEY_INCREASE_SMALL VK_RIGHT
|
||
#define KEY_DECREASE VK_DOWN
|
||
#define KEY_DECREASE_SMALL VK_LEFT
|
||
#define FPS_TARGET 120
|
||
|
||
#include <Windows.h>
|
||
#include <TlHelp32.h>
|
||
#include <vector>
|
||
#include <string>
|
||
#include <thread>
|
||
#include <Psapi.h>
|
||
#include "inireader.h"
|
||
|
||
std::string GamePath{};
|
||
int FpsValue = FPS_TARGET;
|
||
|
||
DWORD StartPriority = 0;
|
||
const std::vector<DWORD> PrioityClass = {
|
||
REALTIME_PRIORITY_CLASS,
|
||
HIGH_PRIORITY_CLASS,
|
||
ABOVE_NORMAL_PRIORITY_CLASS,
|
||
NORMAL_PRIORITY_CLASS,
|
||
BELOW_NORMAL_PRIORITY_CLASS,
|
||
IDLE_PRIORITY_CLASS
|
||
};
|
||
//credit by winTEuser
|
||
const BYTE _shellcode_genshin_Const[] =
|
||
{
|
||
0x00, 0x00, 0x00, 0x00, //uint32_t unlocker_pid _shellcode_genshin[0]
|
||
0x00, 0x00, 0x00, 0x00, //FREE _shellcode_genshin[4]
|
||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //DWORD64 unlocker_FpsValue_addr _shellcode_genshin[8]
|
||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //DWORD64 API_OpenProcess _shellcode_genshin[16]
|
||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //DWORD64 API_ReadProcessmem _shellcode_genshin[24]
|
||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //DWORD64 API_Sleep _shellcode_genshin[32]
|
||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //DWORD64 API_MessageBoxA _shellcode_genshin[40]
|
||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //DWORD64 API_CloseHandle _shellcode_genshin[48]
|
||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //FREE _shellcode_genshin[56]
|
||
//int3
|
||
0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
|
||
0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
|
||
//int3
|
||
0x48, 0x83, 0xEC, 0x38, //sub rsp,0x38 _shellcode_genshin[80] _sync_thread
|
||
0x8B, 0x05, 0xA6, 0xFF, 0xFF, 0xFF, //mov eax,dword[unlocker_pid]
|
||
0x85, 0xC0, //test eax, eax
|
||
0x74, 0x5C, //jz return
|
||
0x41, 0x89, 0xC0, //mov r8d, eax
|
||
0x33, 0xD2, //xor edx, edx
|
||
0xB9, 0xFF, 0xFF, 0x1F, 0x00, //mov ecx,1FFFFF
|
||
0xFF, 0x15, 0xA2, 0xFF, 0xFF, 0xFF, //call [API_OpenProcess]
|
||
0x85, 0xC0, //test eax, eax
|
||
0x74, 0x48, //jz return
|
||
0x89, 0xC6, //mov esi, eax
|
||
0x48, 0x8B, 0x3D, 0x8D, 0xFF, 0xFF, 0xFF,//mov rdi,qword[unlocker_FpsValue_addr]
|
||
0x0F, 0x1F, 0x44, 0x00, 0x00, //nop
|
||
0x89, 0xF1, //mov ecx, esi //Read_tar_fps
|
||
0x48, 0x89, 0xFA, //mov rdx, rdi
|
||
0x4C, 0x8D, 0x05, 0xF8, 0x00, 0x00, 0x00,//lea r8, qword:[Readmem_buffer]
|
||
0x41, 0xB9, 0x04, 0x00, 0x00, 0x00, //mov r9d, 4
|
||
0x31, 0xC0, //xor eax, eax
|
||
0x48, 0x89, 0x44, 0x24, 0x20, //mov qword ptr ss:[rsp+20],rax
|
||
0xFF, 0x15, 0x79, 0xFF, 0xFF, 0xFF, //call [API_ReadProcessmem]
|
||
0x85, 0xC0, //test eax, eax
|
||
0x74, 0x12, //jz Show msg and closehandle
|
||
0xB9, 0xF4, 0x01, 0x00, 0x00, //mov ecx,0x1F4 (500ms)
|
||
0xFF, 0x15, 0x72, 0xFF, 0xFF, 0xFF, //call [API_Sleep]
|
||
0xE8, 0x4D, 0x00, 0x00, 0x00, //call Sync_auto
|
||
0xEB, 0xCB, //jmp Read_tar_fps
|
||
0xE8, 0x66, 0x00, 0x00, 0x00, //call Show Errormsg and CloseHandle
|
||
0x48, 0x83, 0xC4, 0x38, //add rsp,0x38
|
||
0xC3, //return
|
||
0xCC,
|
||
//int3
|
||
0x89, 0x0D, 0xBA, 0x00, 0x00, 0x00, //mov [Game_Current_set], ecx //hook_fps_set _shellcode_genshin[192]
|
||
0x31, 0xC0, //xor eax, eax
|
||
0x83, 0xF9, 0x1E, //cmp ecx, 0x1E
|
||
0x74, 0x0E, //je set 60
|
||
0x83, 0xF9, 0x2D, //cmp ecx, 0x2D
|
||
0x74, 0x15, //je Sync_buffer
|
||
0x90, //nop
|
||
0xB9, 0xE8, 0x03, 0x00, 0x00, //mov ecx, 0x3E8
|
||
0xEB, 0x06, //jmp set
|
||
0xCC, //int3
|
||
0xB9, 0x3C, 0x00, 0x00, 0x00, //mov ecx, 0x3C
|
||
0x89, 0x0D, 0x0B, 0x00, 0x00, 0x00, //mov [hook_fps_get+1], ecx //set
|
||
0xC3, //ret
|
||
0x8B, 0x0D, 0x97, 0x00, 0x00, 0x00, //mov ecx, dword[Readmem_buffer] //Sync_buffer
|
||
0xEB, 0xF1, //jmp set
|
||
0xCC,
|
||
//int3
|
||
0xB8, 0x78, 0x00, 0x00, 0x00, //mov eax,0x78 //hook_fps_get _shellcode_genshin[240]
|
||
0xC3, //ret
|
||
//int3
|
||
0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
|
||
//int3
|
||
0x8B, 0x05, 0x7A, 0x00, 0x00, 0x00, //mov eax, dword[Game_Current_set] //Sync_auto
|
||
0x83, 0xF8, 0x2D, //cmp eax, 0x2D
|
||
0x75, 0x0C, //jne return
|
||
0x8B, 0x05, 0x73, 0x00, 0x00, 0x00, //mov eax, dword[Readmem_buffer]
|
||
0x89, 0x05, 0xDA, 0xFF, 0xFF, 0xFF, //mov dword[hook_fps_get + 1], eax
|
||
0xC3, //ret
|
||
//int3
|
||
0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
|
||
//int3
|
||
0x48, 0x83, 0xEC, 0x28, //sub rsp, 0x28 //Show Errormsg and closehandle
|
||
0x31, 0xC9, //xor ecx, ecx
|
||
0x48, 0x8D, 0x15, 0x23, 0x00, 0x00, 0x00,//lea rdx, qword:["Sync failed!"]
|
||
0x4C, 0x8D, 0x05, 0x2C, 0x00, 0x00, 0x00,//lea r8, qword:["Error"]
|
||
0x41, 0xB9, 0x10, 0x00, 0x00, 0x00, //mov r9d, 0x10
|
||
0xFF, 0x15, 0xE8, 0xFE, 0xFF, 0xFF, //call [API_MessageBoxA]
|
||
0x89, 0xF1, //mov ecx, esi
|
||
0xFF, 0x15, 0xE8, 0xFE, 0xFF, 0xFF, //call [API_CloseHandle]
|
||
0x48, 0x83, 0xC4, 0x28, //add rsp, 0x28
|
||
0xC3, //ret
|
||
//int3
|
||
0xCC, 0xCC, 0xCC,
|
||
'S','y','n','c',' ','f','a','i','l','e','d','!', 0x00, 0x00, 0x00, 0x00,
|
||
'E','r','r','o','r', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
|
||
0x00, 0x00, 0x00, 0x00, //uint32_t Game_Current_set
|
||
0x00, 0x00, 0x00, 0x00, //uint32_t Readmem_buffer
|
||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||
};
|
||
|
||
|
||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> winTEuser
|
||
static uintptr_t PatternScan_Region(uintptr_t startAddress, size_t regionSize, const char* signature)
|
||
{
|
||
auto pattern_to_byte = [](const char* pattern)
|
||
{
|
||
std::vector<int> bytes;
|
||
const char* start = pattern;
|
||
const char* end = pattern + strlen(pattern);
|
||
|
||
for (const char* current = start; current < end; ++current) {
|
||
if (*current == '?') {
|
||
++current;
|
||
if (*current == '?')
|
||
++current;
|
||
bytes.push_back(-1);
|
||
}
|
||
else {
|
||
bytes.push_back(strtoul(current, const_cast<char**>(¤t), 16));
|
||
}
|
||
}
|
||
return bytes;
|
||
};
|
||
|
||
std::vector<int> patternBytes = pattern_to_byte(signature);
|
||
auto scanBytes = reinterpret_cast<std::uint8_t*>(startAddress);
|
||
|
||
for (size_t i = 0; i < regionSize - patternBytes.size(); ++i)
|
||
{
|
||
bool found = true;
|
||
for (size_t j = 0; j < patternBytes.size(); ++j) {
|
||
if (scanBytes[i + j] != patternBytes[j] && patternBytes[j] != -1) {
|
||
found = false;
|
||
break;
|
||
}
|
||
}
|
||
if (found) {
|
||
return (uintptr_t)&scanBytes[i];
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
std::string GetLastErrorAsString(DWORD code)
|
||
{
|
||
LPSTR buf = nullptr;
|
||
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||
NULL, code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&buf, 0, NULL);
|
||
std::string ret = buf;
|
||
LocalFree(buf);
|
||
return ret;
|
||
}
|
||
|
||
bool GetModule2(HANDLE GameHandle, std::string ModuleName, PMODULEENTRY32 pEntry)
|
||
{
|
||
if (!pEntry)
|
||
return false;
|
||
|
||
std::vector<HMODULE> modules(1024);
|
||
ZeroMemory(modules.data(), modules.size() * sizeof(HMODULE));
|
||
DWORD cbNeeded = 0;
|
||
|
||
if (!EnumProcessModules(GameHandle, modules.data(), modules.size() * sizeof(HMODULE), &cbNeeded))
|
||
return false;
|
||
|
||
modules.resize(cbNeeded / sizeof(HMODULE));
|
||
for (auto& it : modules)
|
||
{
|
||
char szModuleName[MAX_PATH]{};
|
||
if (!GetModuleBaseNameA(GameHandle, it, szModuleName, MAX_PATH))
|
||
continue;
|
||
if (ModuleName != szModuleName)
|
||
continue;
|
||
MODULEINFO modInfo{};
|
||
if (!GetModuleInformation(GameHandle, it, &modInfo, sizeof(MODULEINFO)))
|
||
continue;
|
||
|
||
pEntry->modBaseAddr = (BYTE*)modInfo.lpBaseOfDll;
|
||
pEntry->modBaseSize = modInfo.SizeOfImage;
|
||
return true;
|
||
}
|
||
|
||
|
||
return false;
|
||
}
|
||
static bool GetModule(DWORD pid, std::string ModuleName, PMODULEENTRY32 pEntry)
|
||
{
|
||
if (!pEntry)
|
||
return false;
|
||
|
||
MODULEENTRY32 mod32{};
|
||
mod32.dwSize = sizeof(mod32);
|
||
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
|
||
bool temp = Module32First(snap, &mod32);
|
||
if (temp)
|
||
{
|
||
do
|
||
{
|
||
if (mod32.th32ProcessID != pid)
|
||
{
|
||
break;
|
||
}
|
||
if (mod32.szModule == ModuleName)
|
||
{
|
||
*pEntry = mod32;
|
||
CloseHandle(snap);
|
||
return 1;
|
||
}
|
||
|
||
} while (Module32Next(snap, &mod32));
|
||
|
||
}
|
||
CloseHandle(snap);
|
||
return 0;
|
||
}
|
||
// ͨ<><CDA8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ID
|
||
DWORD GetPID(std::string ProcessName)
|
||
{
|
||
DWORD pid = 0;
|
||
PROCESSENTRY32 pe32{};
|
||
pe32.dwSize = sizeof(pe32);
|
||
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||
for (Process32First(snap, &pe32); Process32Next(snap, &pe32);)
|
||
{
|
||
if (pe32.szExeFile == ProcessName)
|
||
{
|
||
pid = pe32.th32ProcessID;
|
||
break;
|
||
}
|
||
}
|
||
CloseHandle(snap);
|
||
return pid;
|
||
}
|
||
|
||
bool WriteConfig(std::string GamePath, int fps)
|
||
{
|
||
HANDLE hFile = CreateFileA("fps_config.ini", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_HIDDEN, nullptr);
|
||
if (hFile == INVALID_HANDLE_VALUE)
|
||
{
|
||
DWORD code = GetLastError();
|
||
printf("CreateFileA failed (%d): %s\n", code, GetLastErrorAsString(code).c_str());
|
||
return false;
|
||
}
|
||
|
||
std::string content{};
|
||
content = "[Setting]\n";
|
||
content += "Path=" + GamePath + "\n";
|
||
content += "FPS=" + std::to_string(fps);
|
||
|
||
DWORD written = 0;
|
||
WriteFile(hFile, content.data(), content.size(), &written, nullptr);
|
||
CloseHandle(hFile);
|
||
}
|
||
//Hotpatch
|
||
static DWORD64 inject_patch(LPVOID text_buffer, DWORD text_size, DWORD64 _text_baseaddr, uint64_t _ptr_fps, HANDLE Tar_handle)
|
||
{
|
||
if (text_buffer == 0 || text_size == 0 || _text_baseaddr == 0 || _ptr_fps == 0 || Tar_handle == 0)
|
||
return 0;
|
||
|
||
DWORD64 Module_TarSec_RVA = (DWORD64)text_buffer;
|
||
DWORD Module_TarSec_Size = text_size;
|
||
|
||
DWORD64 address = 0;
|
||
DWORD64 Hook_addr_fpsget = 0; //in buffer
|
||
DWORD64 Hook_addr_tar_fpsget = 0;
|
||
DWORD64 Hook_addr_fpsSet = 0; //in buffer
|
||
DWORD64 Hook_addr_tar_fpsSet = 0;
|
||
DWORD64 _addr_tar_fpsget_TarFun = 0;
|
||
DWORD64 _addr_tar_fpsSet_TarFun = 0;
|
||
while (address = PatternScan_Region(Module_TarSec_RVA, text_size, "CC 8B 05 ?? ?? ?? ?? C3 CC"))//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȷhook<6F><6B>λ//get_fps
|
||
{
|
||
uintptr_t rip = address;
|
||
rip += 3;
|
||
rip += *(int32_t*)(rip)+4;
|
||
if ((rip - (uintptr_t)Module_TarSec_RVA + (uintptr_t)_text_baseaddr) == _ptr_fps)
|
||
{
|
||
Hook_addr_fpsget = address + 1;
|
||
Hook_addr_tar_fpsget = Hook_addr_fpsget - (uintptr_t)Module_TarSec_RVA + (uintptr_t)_text_baseaddr;
|
||
goto __Get_fpsGet_addr;
|
||
}
|
||
else
|
||
{
|
||
*(uint64_t*)(address + 1) = 0xCCCCCCCCCCCCCCCC;
|
||
}
|
||
}
|
||
printf_s("Get get_fps pattern Fail! \n");
|
||
return 0;
|
||
|
||
__Get_fpsGet_addr:
|
||
while (address = PatternScan_Region(Module_TarSec_RVA, text_size, "CC 89 0D ?? ?? ?? ?? C3 CC"))//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȷhook<6F><6B>λ//set_fps
|
||
{
|
||
uintptr_t rip = address;
|
||
rip += 3;
|
||
rip += *(int32_t*)(rip)+4;
|
||
if ((rip - (uintptr_t)Module_TarSec_RVA + (uintptr_t)_text_baseaddr) == _ptr_fps)
|
||
{
|
||
Hook_addr_fpsSet = address + 1;
|
||
Hook_addr_tar_fpsSet = Hook_addr_fpsSet - (uintptr_t)Module_TarSec_RVA + (uintptr_t)_text_baseaddr;
|
||
goto __Get_fpsSet_addr;
|
||
}
|
||
else
|
||
{
|
||
*(uint64_t*)(address + 1) = 0xCCCCCCCCCCCCCCCC;
|
||
}
|
||
}
|
||
printf_s("Get set_fps pattern Fail! \n");
|
||
|
||
__Get_fpsSet_addr:
|
||
uint64_t _shellcode_buffer = (uint64_t)VirtualAlloc(0, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||
if (_shellcode_buffer == 0)
|
||
{
|
||
printf_s("Buffer Alloc Fail! \n");
|
||
return 0;
|
||
}
|
||
memcpy((void*)_shellcode_buffer, &_shellcode_genshin_Const, sizeof(_shellcode_genshin_Const));
|
||
|
||
uint64_t _Addr_OpenProcess = (uint64_t)(&OpenProcess);
|
||
uint64_t _Addr_ReadProcessmem = (uint64_t)(&ReadProcessMemory);
|
||
uint64_t _Addr_Sleep = (uint64_t)(&Sleep);
|
||
uint64_t _Addr_MessageBoxA = (uint64_t)(&MessageBoxA);
|
||
uint64_t _Addr_CloseHandle = (uint64_t)(&CloseHandle);
|
||
*(uint32_t*)_shellcode_buffer = GetCurrentProcessId(); //unlocker PID
|
||
*(uint64_t*)(_shellcode_buffer + 8) = (uint64_t)(&FpsValue); //unlocker fps ptr
|
||
*(uint64_t*)(_shellcode_buffer + 16) = _Addr_OpenProcess;
|
||
*(uint64_t*)(_shellcode_buffer + 24) = _Addr_ReadProcessmem;
|
||
*(uint64_t*)(_shellcode_buffer + 32) = _Addr_Sleep;
|
||
*(uint64_t*)(_shellcode_buffer + 40) = _Addr_MessageBoxA;
|
||
*(uint64_t*)(_shellcode_buffer + 48) = _Addr_CloseHandle;
|
||
*(uint32_t*)(_shellcode_buffer + 0xD4) = 1000;
|
||
*(uint32_t*)(_shellcode_buffer + 0xDC) = 60;
|
||
LPVOID __Tar_proc_buffer = VirtualAllocEx(Tar_handle, 0, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
|
||
if (__Tar_proc_buffer)
|
||
{
|
||
if (WriteProcessMemory(Tar_handle, __Tar_proc_buffer, (void*)_shellcode_buffer, sizeof(_shellcode_genshin_Const), 0))
|
||
{
|
||
VirtualFree((void*)_shellcode_buffer, 0, MEM_RELEASE);
|
||
_addr_tar_fpsSet_TarFun = (uint64_t)__Tar_proc_buffer + 0xC0;
|
||
_addr_tar_fpsget_TarFun = (uint64_t)__Tar_proc_buffer + 0xF0;
|
||
*(uint64_t*)Hook_addr_fpsget = 0xB848; //hook mov rax,[jmp addr]
|
||
*(uint64_t*)(Hook_addr_fpsget + 2) = _addr_tar_fpsget_TarFun;
|
||
*(uint16_t*)(Hook_addr_fpsget + 10) = 0xE0FF; // jmp rax
|
||
*(uint64_t*)Hook_addr_fpsSet = 0xB848;
|
||
*(uint64_t*)(Hook_addr_fpsSet + 2) = _addr_tar_fpsSet_TarFun;
|
||
*(uint16_t*)(Hook_addr_fpsSet + 10) = 0xE0FF;
|
||
if (WriteProcessMemory(Tar_handle, (LPVOID)Hook_addr_tar_fpsget, (LPVOID)Hook_addr_fpsget, 0x10, 0) == 0)
|
||
{
|
||
DWORD ERR_code = GetLastError();
|
||
printf_s("\nHook Get_fps Fail! ( 0x%X ) - %s\n", ERR_code, GetLastErrorAsString(ERR_code).c_str());
|
||
return 0;
|
||
}
|
||
if (WriteProcessMemory(Tar_handle, (LPVOID)Hook_addr_tar_fpsSet, (LPVOID)Hook_addr_fpsSet, 0x10, 0) == 0)
|
||
{
|
||
DWORD ERR_code = GetLastError();
|
||
printf_s("\nHook Set_fps Fail! ( 0x%X ) - %s\n", ERR_code, GetLastErrorAsString(ERR_code).c_str());
|
||
return 0;
|
||
}
|
||
HANDLE temp = CreateRemoteThread(Tar_handle, 0, 0, (LPTHREAD_START_ROUTINE)((uint64_t)__Tar_proc_buffer + 0x50), 0, 0, 0);
|
||
if (temp)
|
||
{
|
||
CloseHandle(temp);
|
||
}
|
||
else
|
||
{
|
||
printf_s("Create InGame SyncThread Fail! ");
|
||
return 0;
|
||
}
|
||
return ((uint64_t)__Tar_proc_buffer + 0xF1);
|
||
}
|
||
printf_s("Inject shellcode Fail! ");
|
||
VirtualFree((void*)_shellcode_buffer, 0, MEM_RELEASE);
|
||
return 0;
|
||
}
|
||
else
|
||
{
|
||
printf_s("Alloc shellcode space Fail! ");
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
void LoadConfig()
|
||
{
|
||
if (GetFileAttributesA("config") != INVALID_FILE_ATTRIBUTES)
|
||
DeleteFileA("config");
|
||
|
||
INIReader reader("fps_config.ini");
|
||
if (reader.ParseError() != 0)
|
||
{
|
||
printf("<EFBFBD><EFBFBD><EFBFBD>ò<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>\n<EFBFBD>벻Ҫ<EFBFBD>رմ˽<EFBFBD><EFBFBD><EFBFBD> - Ȼ<><C8BB><EFBFBD>ֶ<EFBFBD><D6B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϸ\n<EFBFBD><EFBFBD>ֻ<EFBFBD><EFBFBD>Ҫ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><EFBFBD> - <20><><EFBFBD>ڻ<EFBFBD>ȡ<EFBFBD><C8A1>Ϸ·<CFB7><C2B7>\n");
|
||
printf("\n<EFBFBD>ȴ<EFBFBD><EFBFBD><EFBFBD>Ϸ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>...\n");
|
||
|
||
DWORD pid = 0;
|
||
while (!(pid = GetPID("YuanShen.exe")) && !(pid = GetPID("GenshinImpact.exe")))
|
||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||
|
||
// <20><>ȡ<EFBFBD><C8A1><EFBFBD>̾<EFBFBD><CCBE><EFBFBD> - <20><>Ȩ<EFBFBD>ܵ͵<DCB5><CDB5><EFBFBD> - <20><>Ӧ<EFBFBD>û<EFBFBD>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD>
|
||
HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | SYNCHRONIZE | PROCESS_TERMINATE, FALSE, pid);
|
||
if (!hProcess)
|
||
{
|
||
DWORD code = GetLastError();
|
||
printf("OpenProcess failed (%d): %s", code, GetLastErrorAsString(code).c_str());
|
||
return;
|
||
}
|
||
|
||
char szPath[MAX_PATH]{};
|
||
DWORD length = sizeof(szPath);
|
||
QueryFullProcessImageNameA(hProcess, 0, szPath, &length);
|
||
|
||
GamePath = szPath;
|
||
WriteConfig(GamePath, FpsValue);
|
||
|
||
HWND hwnd = nullptr;
|
||
while (!(hwnd = FindWindowA("UnityWndClass", nullptr)))
|
||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||
|
||
DWORD ExitCode = STILL_ACTIVE;
|
||
while (ExitCode == STILL_ACTIVE)
|
||
{
|
||
SendMessageA(hwnd, WM_CLOSE, 0, 0);
|
||
GetExitCodeProcess(hProcess, &ExitCode);
|
||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||
}
|
||
|
||
// wait for the game to close then continue
|
||
WaitForSingleObject(hProcess, -1);
|
||
CloseHandle(hProcess);
|
||
|
||
system("cls");
|
||
return;
|
||
}
|
||
|
||
GamePath = reader.Get("Setting", "Path", "");
|
||
FpsValue = reader.GetInteger("Setting", "FPS", FpsValue);
|
||
|
||
if (GetFileAttributesA(GamePath.c_str()) == INVALID_FILE_ATTRIBUTES)
|
||
{
|
||
printf("<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϸ·<EFBFBD><EFBFBD><EFBFBD>ı<EFBFBD><EFBFBD><EFBFBD> - <20><>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>\n");
|
||
DeleteFileA("config.ini");
|
||
LoadConfig();
|
||
}
|
||
}
|
||
|
||
// <20>ȼ<EFBFBD><C8BC>߳<EFBFBD>
|
||
DWORD __stdcall Thread1(LPVOID p)
|
||
{
|
||
if (!p)
|
||
return 0;
|
||
|
||
int* pTargetFPS = (int*)p;
|
||
int fps = *pTargetFPS;
|
||
int prev = fps;
|
||
while (true)
|
||
{
|
||
std::this_thread::sleep_for(std::chrono::milliseconds(16));
|
||
if (GetAsyncKeyState(KEY_DECREASE) & 1 && GetAsyncKeyState(VK_RCONTROL) & 0x8000)
|
||
fps -= 20;
|
||
if (GetAsyncKeyState(KEY_DECREASE_SMALL) & 1 && GetAsyncKeyState(VK_RCONTROL) & 0x8000)
|
||
fps -= 2;
|
||
if (GetAsyncKeyState(KEY_INCREASE) & 1 && GetAsyncKeyState(VK_RCONTROL) & 0x8000)
|
||
fps += 20;
|
||
if (GetAsyncKeyState(KEY_INCREASE_SMALL) & 1 && GetAsyncKeyState(VK_RCONTROL) & 0x8000)
|
||
fps += 2;
|
||
if (GetAsyncKeyState(KEY_TOGGLE) & 1)
|
||
fps = fps != 60 ? 60 : prev;
|
||
if (prev != fps)
|
||
WriteConfig(GamePath, fps);
|
||
if (fps > 60)
|
||
prev = fps;
|
||
if (fps < 60)
|
||
fps = 60;
|
||
printf("\rFPS: %d - %s ", fps, fps > 60 ? "ON" : "OFF");
|
||
*pTargetFPS = fps;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
int main(int argc, char** argv)
|
||
{
|
||
std::atexit([] {
|
||
system("pause");
|
||
});
|
||
|
||
SetConsoleTitleA("");
|
||
|
||
std::string CommandLine{};
|
||
if (argc > 1)
|
||
{
|
||
for (int i = 1; i < argc; i++)
|
||
CommandLine += argv[i] + std::string(" ");
|
||
}
|
||
|
||
// <20><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD>
|
||
LoadConfig();
|
||
int TargetFPS = FpsValue;
|
||
std::string ProcessPath = GamePath;
|
||
std::string ProcessDir{};
|
||
|
||
if (ProcessPath.length() < 8)
|
||
return 0;
|
||
|
||
printf("FPS<EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>õĻ<C3B5><C4BB><EFBFBD><EFBFBD><EFBFBD>star<61><72>\n");
|
||
printf("https://github.com/xiaonian233/genshin-fps-unlock \n4.7<EFBFBD>汾<EFBFBD>ر<EFBFBD><EFBFBD><EFBFBD>лwinTEuser<EFBFBD>ϸ<EFBFBD>֧<EFBFBD><EFBFBD> \n");
|
||
printf("<EFBFBD><EFBFBD>Ϸ·<EFBFBD><EFBFBD>: %s\n\n", ProcessPath.c_str());
|
||
ProcessDir = ProcessPath.substr(0, ProcessPath.find_last_of("\\"));
|
||
std::string procname = ProcessPath.substr(ProcessPath.find_last_of("\\") + 1);
|
||
|
||
DWORD pid = GetPID(procname);
|
||
if (pid)
|
||
{
|
||
printf("<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϸ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>У<EFBFBD>\n");
|
||
printf("<EFBFBD>ֶ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϸ<EFBFBD>ᵼ<EFBFBD><EFBFBD>ʧЧ<EFBFBD><EFBFBD>\n");
|
||
printf("<EFBFBD><EFBFBD><EFBFBD>ֶ<EFBFBD><EFBFBD>ر<EFBFBD><EFBFBD><EFBFBD>Ϸ - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Զ<EFBFBD><D4B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϸ\n");
|
||
return 0;
|
||
}
|
||
|
||
STARTUPINFOA si{};
|
||
PROCESS_INFORMATION pi{};
|
||
if (!CreateProcessA(ProcessPath.c_str(), (LPSTR)CommandLine.c_str(), nullptr, nullptr, FALSE, 0, nullptr, ProcessDir.c_str(), &si, &pi))
|
||
{
|
||
DWORD code = GetLastError();
|
||
printf("CreateProcess failed (%d): %s", code, GetLastErrorAsString(code).c_str());
|
||
return 0;
|
||
}
|
||
|
||
CloseHandle(pi.hThread);
|
||
printf("PID: %d\n", pi.dwProcessId);
|
||
Sleep(200);
|
||
StartPriority = PrioityClass[1];
|
||
SetPriorityClass(pi.hProcess, StartPriority);
|
||
// <20>ȴ<EFBFBD>UnityPlayer.dll<6C><6C><EFBFBD>غͻ<D8BA>ȡDLL<4C><4C>Ϣ
|
||
MODULEENTRY32 hUnityPlayer{};
|
||
{
|
||
DWORD times = 1000;
|
||
while (times != 0)
|
||
{
|
||
if (GetModule2(pi.hProcess, procname, &hUnityPlayer))
|
||
{
|
||
goto __get_procbase_ok;
|
||
}
|
||
Sleep(50);
|
||
times -= 5;
|
||
}
|
||
printf("Get BaseModule time out! \n");
|
||
CloseHandle(pi.hProcess);
|
||
return (int)-1;
|
||
}
|
||
{
|
||
DWORD times = 1000;
|
||
while (!GetModule2(pi.hProcess, "UnityPlayer.dll", &hUnityPlayer))
|
||
{
|
||
Sleep(50);
|
||
times -= 5;
|
||
if (GetModule2(pi.hProcess, "unityplayer.dll", &hUnityPlayer))
|
||
{
|
||
goto __get_procbase_ok;
|
||
}
|
||
if (times == 0)
|
||
{
|
||
printf("Get Unitymodule time out! \n");
|
||
CloseHandle(pi.hProcess);
|
||
return (int)-1;
|
||
}
|
||
}
|
||
}
|
||
printf("UnityPlayer: %X%X\n", (uintptr_t)hUnityPlayer.modBaseAddr >> 32 & -1, hUnityPlayer.modBaseAddr);
|
||
__get_procbase_ok:
|
||
LPVOID _mbase_PE_buffer = VirtualAlloc(NULL, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||
if (_mbase_PE_buffer == 0)
|
||
{
|
||
printf_s("VirtualAlloc Failed! (PE_buffer)");
|
||
CloseHandle(pi.hProcess);
|
||
return (int)-1;
|
||
}
|
||
if (hUnityPlayer.modBaseAddr == 0)
|
||
return (int)-1;
|
||
if (ReadProcessMemory(pi.hProcess, hUnityPlayer.modBaseAddr, _mbase_PE_buffer, 0x1000, 0) == 0)
|
||
{
|
||
printf_s("Readmem Failed! (PE_buffer)");
|
||
VirtualFree(_mbase_PE_buffer, 0, MEM_RELEASE);
|
||
CloseHandle(pi.hProcess);
|
||
return (int)-1;
|
||
}
|
||
BYTE search_sec[8] = ".text";//max 8 byte
|
||
uint64_t tar_sec = *(uint64_t*)&search_sec;
|
||
uintptr_t WinPEfileVA = *(uintptr_t*)(&_mbase_PE_buffer) + 0x3c; //dos_header
|
||
uintptr_t PEfptr = *(uintptr_t*)(&_mbase_PE_buffer) + *(uint32_t*)WinPEfileVA; //get_winPE_VA
|
||
_IMAGE_NT_HEADERS64 _FilePE_Nt_header = *(_IMAGE_NT_HEADERS64*)PEfptr;
|
||
_IMAGE_SECTION_HEADER _sec_temp{};
|
||
uintptr_t Text_Remote_RVA;
|
||
uint32_t Text_Vsize;
|
||
if (_FilePE_Nt_header.Signature == 0x00004550)
|
||
{
|
||
DWORD sec_num = _FilePE_Nt_header.FileHeader.NumberOfSections;//<2F><><EFBFBD><EFBFBD>ָ<EFBFBD><D6B8><EFBFBD>ڶβ<DAB6><CEB2><EFBFBD>
|
||
DWORD num = sec_num;
|
||
DWORD target_sec_VA_start = 0;
|
||
while (num)
|
||
{
|
||
_sec_temp = *(_IMAGE_SECTION_HEADER*)(PEfptr + 264 + (40 * (static_cast<unsigned long long>(sec_num) - num)));
|
||
|
||
//printf_s("sec_%d_is: %s\n", sec_num - num, _sec_temp.Name);
|
||
|
||
if (*(uint64_t*)(_sec_temp.Name) == tar_sec)
|
||
{
|
||
target_sec_VA_start = _sec_temp.VirtualAddress;
|
||
Text_Vsize = _sec_temp.Misc.VirtualSize;
|
||
Text_Remote_RVA = (uintptr_t)hUnityPlayer.modBaseAddr + target_sec_VA_start;
|
||
goto __Get_target_sec;
|
||
}
|
||
num--;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
printf_s("Invalid PE header!");
|
||
VirtualFree(_mbase_PE_buffer, 0, MEM_RELEASE);
|
||
CloseHandle(pi.hProcess);
|
||
return (int)-1;
|
||
}
|
||
__Get_target_sec:
|
||
// <20>ڱ<EFBFBD><DAB1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>δ<EFBFBD>С<EFBFBD><D0A1><EFBFBD>ڴ<EFBFBD> - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
LPVOID Copy_Text_VA = VirtualAlloc(0, Text_Vsize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||
if (Copy_Text_VA == NULL)
|
||
{
|
||
printf("VirtualAlloc Failed! (Text)");
|
||
CloseHandle(pi.hProcess);
|
||
return (int)-1;
|
||
}
|
||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ģ<EFBFBD><C4A3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
if (ReadProcessMemory(pi.hProcess, (void*)Text_Remote_RVA, Copy_Text_VA, Text_Vsize, 0) == 0)
|
||
{
|
||
printf("Readmem Fail ! (text)");
|
||
VirtualFree(Copy_Text_VA, 0, MEM_RELEASE);
|
||
CloseHandle(pi.hProcess);
|
||
return (int)-1;
|
||
}
|
||
|
||
printf("Searching for pattern...\n");
|
||
|
||
//credit by winTEuser
|
||
uintptr_t address = PatternScan_Region((uintptr_t)Copy_Text_VA, Text_Vsize, "7F 0E E8 ?? ?? ?? ?? 66 0F 6E C8"); // ver 3.7 - last
|
||
if (!address)
|
||
{
|
||
printf("outdated pattern\n");
|
||
return 0;
|
||
}
|
||
|
||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ե<EFBFBD>ַ (FPS)
|
||
uintptr_t pfps = 0;
|
||
{
|
||
uintptr_t rip = address;
|
||
rip += 3;
|
||
rip += *(int32_t*)(rip)+6;
|
||
rip += *(int32_t*)(rip)+4;
|
||
pfps = rip - (uintptr_t)Copy_Text_VA + Text_Remote_RVA;
|
||
printf("FPS Offset: %X\n", pfps);
|
||
}
|
||
uintptr_t Patch_ptr = 0;
|
||
{
|
||
Patch_ptr = inject_patch(Copy_Text_VA, Text_Vsize, Text_Remote_RVA, pfps, pi.hProcess);//patch inject
|
||
if (Patch_ptr == NULL)
|
||
{
|
||
printf_s("Inject Patch Fail!\n\n");
|
||
}
|
||
}
|
||
|
||
VirtualFree(_mbase_PE_buffer, 0, MEM_RELEASE);
|
||
VirtualFree(Copy_Text_VA, 0, MEM_RELEASE);
|
||
printf("Done\n\n");
|
||
printf("<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ctrl + <20><>ͷ<EFBFBD><CDB7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>:\n");
|
||
printf(" <20><>ctrl + <20><>: +20\n");
|
||
printf(" <20><>ctrl + <20><>: -20\n");
|
||
printf(" <20><>ctrl + <20><>: -2\n");
|
||
printf(" <20><>ctrl + <20><>: +2\n\n");
|
||
|
||
// <20><><EFBFBD><EFBFBD><EFBFBD>ȼ<EFBFBD><C8BC>߳<EFBFBD>
|
||
HANDLE hThread = CreateThread(nullptr, 0, Thread1, &TargetFPS, 0, nullptr);
|
||
if (hThread)
|
||
CloseHandle(hThread);
|
||
|
||
DWORD dwExitCode = STILL_ACTIVE;
|
||
while (dwExitCode == STILL_ACTIVE)
|
||
{
|
||
GetExitCodeProcess(pi.hProcess, &dwExitCode);
|
||
|
||
// ÿ<><C3BF><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><D2BB>
|
||
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||
int fps = 0;
|
||
ReadProcessMemory(pi.hProcess, (LPVOID)pfps, &fps, sizeof(fps), nullptr);
|
||
if (fps == -1)
|
||
continue;
|
||
if (fps != TargetFPS)
|
||
WriteProcessMemory(pi.hProcess, (LPVOID)pfps, &TargetFPS, sizeof(TargetFPS), nullptr);
|
||
|
||
|
||
}
|
||
|
||
CloseHandle(pi.hProcess);
|
||
TerminateProcess((HANDLE)-1, 0);
|
||
|
||
return 0;
|
||
}
|