Files
genshin-fps-unlock/unlockfps/main.cpp

713 lines
26 KiB
C++
Raw Normal View History

2023-05-25 14:15:06 +08:00
#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;
2024-06-13 09:51:05 +08:00
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
};
2024-06-27 13:44:52 +08:00
//credit by winTEuser
BYTE _shellcode_genshin[] =
{
2024-06-27 19:42:22 +08:00
0x00, 0x00, 0x00, 0x00, // uint32_t unlocker_pid _shellcode_genshin[0]
0x00, 0x00, 0x00, 0x00, // uint32_t unlocker_Handle _shellcode_genshin[4]
2024-06-27 13:44:52 +08:00
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]
2024-06-27 19:42:22 +08:00
0x00, 0x00, 0x00, 0x00, //uint32_t Readmem_buffer _shellcode_genshin[40]
2024-06-27 13:44:52 +08:00
0xCC, 0xCC, 0xCC, 0xCC, //int3
0x48, 0x83, 0xEC, 0x38, //sub rsp,0x38 _shellcode_genshin[48] _sync_thread
0x8B, 0x05, 0xC6, 0xFF, 0xFF, 0xFF, //mov eax,dword[unlocker_pid]
0x85, 0xC0, //test eax
2024-06-27 19:42:22 +08:00
0x74, 0x5B, //je return
2024-06-27 13:44:52 +08:00
0x41, 0x89, 0xC0, //mov r8d,eax
0x33, 0xD2, //xor edx,edx
0xB9, 0xFF, 0xFF, 0x1F, 0x00, //mov ecx,1FFFFF
0xFF, 0x15, 0xC2, 0xFF, 0xFF, 0xFF, //call [API_OpenProcess]
0x85, 0xC0, //test eax
2024-06-27 19:42:22 +08:00
0x74, 0x47, //je return
2024-06-27 13:44:52 +08:00
0x89, 0x05, 0xAC, 0xFF, 0xFF, 0xFF, //mov dword[unlocker_Handle],eax
0x89, 0xC6, //mov esi,eax
2024-06-27 19:42:22 +08:00
0x48, 0x8B, 0x3D, 0xA7, 0xFF, 0xFF, 0xFF,//mov rdi,qword[unlocker_FpsValue_addr]
2024-06-27 13:44:52 +08:00
0x0F, 0x1F, 0x00, //nop
0x89, 0xF1, //mov ecx,esi //Read_tar_fps
0x48, 0x89, 0xFA, //mov rdx,rdi
0x4C, 0x8D, 0x05, 0xB8, 0xFF, 0xFF, 0xFF,//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, 0x95, 0xFF, 0xFF, 0xFF, //call [API_ReadProcessmem]
0x85, 0xC0, //test eax
2024-06-27 19:42:22 +08:00
0x74, 0x12, //jz return
0xB9, 0xE8, 0x03, 0x00, 0x00, //mov ecx,0x3E8 (1000ms)
0xFF, 0x15, 0x8E, 0xFF, 0xFF, 0xFF, //call [API_Sleep]
0xE8, 0x49, 0x00, 0x00, 0x00, //call Sync_Set
0xEB, 0xCB, //jmp Read_tar_fps
0x48, 0x83, 0xC4, 0x38, //add rsp,0x38
2024-06-27 13:44:52 +08:00
0xC3, //ret
2024-06-27 19:42:22 +08:00
0xCC, 0xCC, //int3
0x89, 0x0D, 0x22, 0x00, 0x00, 0x00, //mov [Game_Current_set], ecx //hook_fps_set _shellcode_genshin[160]
0xEB, 0x00, //nop
0x83, 0xF9, 0x1E, //cmp ecx, 0x1E
0x74, 0x0C, //je set 60
0x83, 0xF9, 0x2D, //cmp ecx, 0x2D
0x74, 0x12, //je return
0xB9, 0xFF, 0xFF, 0xFF, 0xFF, //mov ecx,[Readmem_buffer]
2024-06-27 13:44:52 +08:00
0xEB, 0x05, //jmp set
0xB9, 0x3C, 0x00, 0x00, 0x00, //mov ecx,0x3C
2024-06-27 19:42:22 +08:00
0x89, 0x0D, 0x0D, 0x00, 0x00, 0x00, //mov [hook_fps_get + 1],ecx
2024-06-27 13:44:52 +08:00
0xC3, //ret
2024-06-27 19:42:22 +08:00
0xCC, 0xCC, 0xCC, //int3
0x00, 0x00, 0x00, 0x00, //uint32_t Game_Current_set
0xCC, 0xCC, 0xCC, 0xCC, //int3
2024-06-27 13:44:52 +08:00
0xB8,0x78, 0x00, 0x00, 0x00, //mov eax,0x78 //hook_fps_get _shellcode_genshin[208]
0xC3, //ret
2024-06-27 19:42:22 +08:00
0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,//int3
0x56, //push rsi //Sync_Set
0x57, //push rdi
0x48, 0x83, 0xEC, 0x18, //sub rsp, 0x18
0x8B, 0x05, 0xDC, 0xFF, 0xFF, 0xFF, //mov eax, dword[Game_Current_set]
0x83, 0xF8, 0x2D, //cmp eax, 0x2D
0x75, 0x0C, //jne return
0x8B, 0x05, 0x31, 0xFF, 0xFF, 0xFF, //mov eax, dword[Game_Current_set]
0x89, 0x05, 0xD4, 0xFF, 0xFF, 0xFF, //mov dword[hook_fps_get + 1], eax
0x48, 0x83, 0xC4, 0x18, //add rsp, 0x18
0x5F, //pop rdi
0x5E, //pop rsi
0xC3, //ret
0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC
2024-06-27 13:44:52 +08:00
};
2024-06-13 09:51:05 +08:00
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>д<EFBFBD><D0B4> - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŀ<EFBFBD><C4BF><EFBFBD>
2023-05-25 14:15:06 +08:00
uintptr_t PatternScan(void* module, const char* signature)
{
static auto pattern_to_byte = [](const char* pattern) {
auto bytes = std::vector<int>{};
auto start = const_cast<char*>(pattern);
auto end = const_cast<char*>(pattern) + strlen(pattern);
for (auto current = start; current < end; ++current) {
if (*current == '?') {
++current;
if (*current == '?')
++current;
bytes.push_back(-1);
}
else {
bytes.push_back(strtoul(current, &current, 16));
}
}
return bytes;
};
auto dosHeader = (PIMAGE_DOS_HEADER)module;
auto ntHeaders = (PIMAGE_NT_HEADERS)((std::uint8_t*)module + dosHeader->e_lfanew);
auto sizeOfImage = ntHeaders->OptionalHeader.SizeOfImage;
auto patternBytes = pattern_to_byte(signature);
auto scanBytes = reinterpret_cast<std::uint8_t*>(module);
auto s = patternBytes.size();
auto d = patternBytes.data();
for (auto i = 0ul; i < sizeOfImage - s; ++i) {
bool found = true;
for (auto j = 0ul; j < s; ++j) {
if (scanBytes[i + j] != d[j] && d[j] != -1) {
found = false;
break;
}
}
if (found) {
return (uintptr_t)&scanBytes[i];
}
}
return 0;
}
2024-06-27 13:44:52 +08:00
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;
}
2023-05-25 14:15:06 +08:00
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;
}
2024-06-13 09:51:05 +08:00
bool GetModule2(HANDLE GameHandle, std::string ModuleName, PMODULEENTRY32 pEntry)
2023-05-25 14:15:06 +08:00
{
if (!pEntry)
return false;
2024-06-13 09:51:05 +08:00
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)
2023-05-25 14:15:06 +08:00
{
2024-06-13 09:51:05 +08:00
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)))
2023-05-25 14:15:06 +08:00
continue;
2024-06-13 09:51:05 +08:00
pEntry->modBaseAddr = (BYTE*)modInfo.lpBaseOfDll;
pEntry->modBaseSize = modInfo.SizeOfImage;
return true;
2023-05-25 14:15:06 +08:00
}
2024-06-13 09:51:05 +08:00
return false;
}
// ͨ<><CDA8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ID
2023-05-25 14:15:06 +08:00
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)
{
2023-11-27 10:58:37 +08:00
HANDLE hFile = CreateFileA("fps_config.ini", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_HIDDEN, nullptr);
2023-05-25 14:15:06 +08:00
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);
}
2024-06-27 13:44:52 +08:00
//Hotpatch
static DWORD64 inject_patch(LPVOID unity_module, DWORD64 unity_baseaddr, DWORD64 _ptr_fps, HANDLE Tar_handle)
{
BYTE search_sec[] = ".text";//max 8 byte
uintptr_t WinPEfileVA = *(uintptr_t*)(&unity_module) + 0x3c; //dos_header
uintptr_t PEfptr = *(uintptr_t*)(&unity_module) + *(uint32_t*)WinPEfileVA; //get_winPE_VA
_IMAGE_NT_HEADERS64 _FilePE_Nt_header = *(_IMAGE_NT_HEADERS64*)PEfptr;
_IMAGE_SECTION_HEADER _sec_temp{};
DWORD64 Module_TarSec_RVA;
DWORD64 Module_TarSecEnd_RVA;
DWORD Module_TarSec_Size;
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;
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);
int i = 8;
int len = sizeof(search_sec) - 1;
int cmp = 0;
while ((i != 0) && _sec_temp.Name[8 - i] && search_sec[8 - i])
{
if (_sec_temp.Name[8 - i] == search_sec[8 - i])
{
cmp++;
}
i--;
}
if (cmp == len)
{
Module_TarSec_RVA = _sec_temp.VirtualAddress + (DWORD64)unity_module;
Module_TarSec_Size = _sec_temp.Misc.VirtualSize;
Module_TarSecEnd_RVA = Module_TarSec_RVA + Module_TarSec_Size;
goto __Get_target_sec;
}
num--;
}
printf_s("Get Target Section Fail !\n");
return 0;
}
return 0;
__Get_target_sec:
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, Module_TarSec_Size, "CC 8B 05 ?? ?? ?? ?? C3 CC"))//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȷpatch<63><68>λ//get_fps
{
uintptr_t rip = address;
rip += 3;
rip += *(int32_t*)(rip)+4;
if ((rip - (uintptr_t)unity_module + (uintptr_t)unity_baseaddr) == _ptr_fps)
{
Hook_addr_fpsget = address + 1;
Hook_addr_tar_fpsget = Hook_addr_fpsget - (uintptr_t)unity_module + (uintptr_t)unity_baseaddr;
goto __Get_fpsGet_addr;
}
else
{
*(uint64_t*)(address + 1) = 0xCCCCCCCCCCCCCCCC;
}
}
printf_s("\nPatch pattern1 outdate...\n");
return 0;
__Get_fpsGet_addr:
while (address = PatternScan_Region(Module_TarSec_RVA, Module_TarSec_Size, "CC 89 0D ?? ?? ?? ?? C3 CC"))//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȷpatch<63><68>λ//set_fps
{
uintptr_t rip = address;
rip += 3;
rip += *(int32_t*)(rip)+4;
if ((rip - (uintptr_t)unity_module + (uintptr_t)unity_baseaddr) == _ptr_fps)
{
Hook_addr_fpsSet = address + 1;
Hook_addr_tar_fpsSet = Hook_addr_fpsSet - (uintptr_t)unity_module + (uintptr_t)unity_baseaddr;
goto __Get_fpsSet_addr;
}
else
{
*(uint64_t*)(address + 1) = 0xCCCCCCCCCCCCCCCC;
}
}
printf_s("\nPatch pattern2 outdate...\n");
return 0;
__Get_fpsSet_addr:
uint64_t _Addr_OpenProcess = 0;
uint64_t _Addr_ReadProcessmem = 0;
uint64_t _Addr_Sleep = 0;
if (address = PatternScan_Region(Module_TarSec_RVA, Module_TarSec_Size, "33 D2 B9 00 04 00 00 FF 15 ?? ?? ?? ??"))//get API OpenProcess
{
uintptr_t rip = address;
rip += 9;
rip += *(int32_t*)(rip)+4;
if (*(uint64_t*)(rip) == 0)
{
rip = rip - (uintptr_t)unity_module + (uintptr_t)unity_baseaddr;
while (_Addr_OpenProcess == 0)
{
if (ReadProcessMemory(Tar_handle, (LPCVOID)rip, &_Addr_OpenProcess, 8, 0) == 0)
{
DWORD ERR_code = GetLastError();
printf_s("\nGet Target Openprocess API Fail! ( 0x%X ) - %s\n", ERR_code, GetLastErrorAsString(ERR_code).c_str());
return 0;
}
}
}
else { _Addr_OpenProcess = *(uint64_t*)rip; }
}
if (address = PatternScan_Region(Module_TarSec_RVA, Module_TarSec_Size, "48 89 44 24 20 FF 15 ?? ?? ?? ?? 48 8B 54 24 70"))//get API ReadProcmem
{
uintptr_t rip = address;
rip += 7;
rip += *(int32_t*)(rip)+4;
if (*(uint64_t*)(rip) == 0)
{
rip = rip - (uintptr_t)unity_module + (uintptr_t)unity_baseaddr;
while (_Addr_ReadProcessmem == 0)
{
if (ReadProcessMemory(Tar_handle, (LPCVOID)rip, &_Addr_ReadProcessmem, 8, 0) == 0)
{
DWORD ERR_code = GetLastError();
printf_s("\nGet Target Readprocmem API Fail! ( 0x%X ) - %s\n", ERR_code, GetLastErrorAsString(ERR_code).c_str());
return 0;
}
}
}
else { _Addr_ReadProcessmem = *(uint64_t*)rip; }
}
if (address = PatternScan_Region(Module_TarSec_RVA, Module_TarSec_Size, "41 8B C8 FF 15 ?? ?? ?? ?? 8B C7"))//get API Sleep
{
uintptr_t rip = address;
rip += 5;
rip += *(int32_t*)(rip)+4;
if (*(uint64_t*)(rip) == 0)
{
rip = rip - (uintptr_t)unity_module + (uintptr_t)unity_baseaddr;
while (_Addr_Sleep == 0)
{
if (ReadProcessMemory(Tar_handle, (LPCVOID)rip, &_Addr_Sleep, 8, 0) == 0)
{
DWORD ERR_code = GetLastError();
printf_s("\nGet Target Sleep API Fail! ( 0x%X ) - %s\n", ERR_code, GetLastErrorAsString(ERR_code).c_str());
return 0;
}
}
}
else { _Addr_Sleep = *(uint64_t*)rip; }
}
*(uint32_t*)(&_shellcode_genshin) = GetCurrentProcessId(); //unlocker PID
*(uint64_t*)(&_shellcode_genshin[8]) = (uint64_t)(&FpsValue); //unlocker fps set
*(uint64_t*)(&_shellcode_genshin[16]) = _Addr_OpenProcess;
*(uint64_t*)(&_shellcode_genshin[24]) = _Addr_ReadProcessmem;
*(uint64_t*)(&_shellcode_genshin[32]) = _Addr_Sleep;
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, &_shellcode_genshin, sizeof(_shellcode_genshin), 0));
{
_addr_tar_fpsSet_TarFun = (uint64_t)__Tar_proc_buffer + 160;
_addr_tar_fpsget_TarFun = (uint64_t)__Tar_proc_buffer + 208;
*(uint64_t*)Hook_addr_fpsget = 0xCCCCCCCCCCCCCCCC;
*(uint64_t*)Hook_addr_fpsSet = 0xCCCCCCCCCCCCCCCC;
*(uint64_t*)Hook_addr_fpsget = 0x25FF;
*(uint64_t*)(Hook_addr_fpsget + 6) = _addr_tar_fpsget_TarFun;
*(uint64_t*)Hook_addr_fpsSet = 0x25FF;
*(uint64_t*)(Hook_addr_fpsSet + 6) = _addr_tar_fpsSet_TarFun;
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());
}
if (WriteProcessMemory(Tar_handle, (LPVOID)Hook_addr_tar_fpsSet, (LPVOID)Hook_addr_fpsSet, 0x10, 0) == 0)
{
DWORD ERR_code = GetLastError();
printf_s("\nHook get_fps Fail! ( 0x%X ) - %s\n", ERR_code, GetLastErrorAsString(ERR_code).c_str());
}
HANDLE temp = CreateRemoteThread(Tar_handle, 0, 0, (LPTHREAD_START_ROUTINE)((uint64_t)__Tar_proc_buffer + 0x30), 0, 0, 0);
if (temp)
{
CloseHandle(temp);
}
return ((uint64_t)__Tar_proc_buffer + 0xD1);
}
DWORD ERR_code = GetLastError();
printf_s("\nWrite Patch Fail! ( 0x%X ) - %s\n", ERR_code, GetLastErrorAsString(ERR_code).c_str());
}
else
{
DWORD ERR_code = GetLastError();
printf_s("\nVirtual Alloc Fail! ( 0x%X ) - %s\n", ERR_code, GetLastErrorAsString(ERR_code).c_str());
return 0;
}
}
}
2023-05-25 14:15:06 +08:00
void LoadConfig()
{
if (GetFileAttributesA("config") != INVALID_FILE_ATTRIBUTES)
DeleteFileA("config");
INIReader reader("fps_config.ini");
if (reader.ParseError() != 0)
{
2024-06-13 09:51:05 +08:00
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");
2023-05-25 14:15:06 +08:00
DWORD pid = 0;
while (!(pid = GetPID("YuanShen.exe")) && !(pid = GetPID("GenshinImpact.exe")))
std::this_thread::sleep_for(std::chrono::milliseconds(200));
2024-06-13 09:51:05 +08:00
// <20><>ȡ<EFBFBD><C8A1><EFBFBD>̾<EFBFBD><CCBE><EFBFBD> - <20><>Ȩ<EFBFBD>޺ܵ͵<DCB5><CDB5><EFBFBD> - <20><>Ӧ<EFBFBD>û<EFBFBD>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD>
// PROCESS_QUERY_LIMITED_INFORMATION - <20><><EFBFBD>ڲ<EFBFBD>ѯ<EFBFBD><D1AF><EFBFBD><EFBFBD>·<EFBFBD><C2B7> (K32GetModuleFileNameExA)
// SYNCHRONIZE - <20><><EFBFBD>ڵȴ<DAB5><C8B4><EFBFBD><EFBFBD>̽<EFBFBD><CCBD><EFBFBD> (WaitForSingleObject)
HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | SYNCHRONIZE | PROCESS_TERMINATE, FALSE, pid);
2023-05-25 14:15:06 +08:00
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)
{
2024-06-13 09:51:05 +08:00
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");
2023-05-25 14:15:06 +08:00
DeleteFileA("config.ini");
LoadConfig();
}
}
2024-06-13 09:51:05 +08:00
// <20>ȼ<EFBFBD><C8BC>߳<EFBFBD>
2023-05-25 14:15:06 +08:00
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(" ");
}
2024-06-13 09:51:05 +08:00
// <20><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD>
2023-05-25 14:15:06 +08:00
LoadConfig();
int TargetFPS = FpsValue;
std::string ProcessPath = GamePath;
std::string ProcessDir{};
if (ProcessPath.length() < 8)
return 0;
2024-06-13 09:51:05 +08:00
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());
2023-05-25 14:15:06 +08:00
ProcessDir = ProcessPath.substr(0, ProcessPath.find_last_of("\\"));
DWORD pid = GetPID(ProcessPath.substr(ProcessPath.find_last_of("\\") + 1));
if (pid)
{
2024-06-13 09:51:05 +08:00
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");
2023-05-25 14:15:06 +08:00
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);
2024-06-27 13:44:52 +08:00
StartPriority = PrioityClass[1];
2024-06-13 09:51:05 +08:00
SetPriorityClass(pi.hProcess, StartPriority);
2023-05-25 14:15:06 +08:00
2024-06-13 09:51:05 +08:00
// <20>ȴ<EFBFBD>UnityPlayer.dll<6C><6C><EFBFBD>غͻ<D8BA>ȡDLL<4C><4C>Ϣ
2023-05-25 14:15:06 +08:00
MODULEENTRY32 hUnityPlayer{};
2024-06-13 09:51:05 +08:00
while (!GetModule2(pi.hProcess, "UnityPlayer.dll", &hUnityPlayer))
2023-05-25 14:15:06 +08:00
std::this_thread::sleep_for(std::chrono::milliseconds(100));
printf("UnityPlayer: %X%X\n", (uintptr_t)hUnityPlayer.modBaseAddr >> 32 & -1, hUnityPlayer.modBaseAddr);
2024-06-13 09:51:05 +08:00
// <20>ڱ<EFBFBD><DAB1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>UnityPlayer.dll<6C><6C>С<EFBFBD><D0A1><EFBFBD>ڴ<EFBFBD> - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
LPVOID up = VirtualAlloc(nullptr, hUnityPlayer.modBaseSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
2023-05-25 14:15:06 +08:00
if (!up)
{
DWORD code = GetLastError();
2024-06-13 09:51:05 +08:00
printf("VirtualAlloc UP failed (%d): %s", code, GetLastErrorAsString(code).c_str());
return 0;
}
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ģ<EFBFBD><C4A3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
if (!ReadProcessMemory(pi.hProcess, hUnityPlayer.modBaseAddr, up, hUnityPlayer.modBaseSize, nullptr))
{
DWORD code = GetLastError();
printf("ReadProcessMemory unity failed (%d): %s", code, GetLastErrorAsString(code).c_str());
2023-05-25 14:15:06 +08:00
return 0;
}
2024-06-13 09:51:05 +08:00
2023-05-25 14:15:06 +08:00
printf("Searching for pattern...\n");
2023-12-20 12:50:52 +08:00
2024-06-13 09:51:05 +08:00
//credit by winTEuser
uintptr_t address = PatternScan(up, "7F 0E E8 ?? ?? ?? ?? 66 0F 6E C8"); // ver 3.7 - last
2023-05-25 14:15:06 +08:00
if (!address)
{
2024-06-13 09:51:05 +08:00
printf("outdated pattern\n");
return 0;
2023-05-25 14:15:06 +08:00
}
2024-06-13 09:51:05 +08:00
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ե<EFBFBD>ַ (FPS)
2023-05-25 14:15:06 +08:00
uintptr_t pfps = 0;
{
uintptr_t rip = address;
2024-06-13 09:51:05 +08:00
rip += 3;
rip += *(int32_t*)(rip)+6;
rip += *(int32_t*)(rip)+4;
pfps = rip - (uintptr_t)up + (uintptr_t)hUnityPlayer.modBaseAddr;
2023-05-25 14:15:06 +08:00
printf("FPS Offset: %X\n", pfps);
}
2024-06-27 13:44:52 +08:00
uintptr_t Patch_ptr = 0;
{
Patch_ptr = inject_patch(up, (DWORD64)hUnityPlayer.modBaseAddr, pfps, pi.hProcess);//45 patch config
if (Patch_ptr == NULL)
{
printf_s("Inject Patch Fail!\n\n");
}
}
2023-05-25 14:15:06 +08:00
VirtualFree(up, 0, MEM_RELEASE);
printf("Done\n\n");
2024-06-13 09:51:05 +08:00
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");
2023-05-25 14:15:06 +08:00
2024-06-13 09:51:05 +08:00
// <20><><EFBFBD><EFBFBD><EFBFBD>ȼ<EFBFBD><C8BC>߳<EFBFBD>
2023-05-25 14:15:06 +08:00
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);
2024-06-13 09:51:05 +08:00
// ÿ<><C3BF><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><D2BB>
2023-05-25 14:15:06 +08:00
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);
2024-06-27 13:44:52 +08:00
2023-05-25 14:15:06 +08:00
}
CloseHandle(pi.hProcess);
TerminateProcess((HANDLE)-1, 0);
return 0;
2023-11-27 10:58:37 +08:00
}