Files
genshin-fps-unlock/unlockfps/main.cpp
2024-07-17 11:57:53 +08:00

726 lines
26 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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**>(&current), 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;
}