This commit is contained in:
“RiceCake”
2023-05-25 14:15:06 +08:00
commit 5520bc345e
7 changed files with 1132 additions and 0 deletions

415
unlockfps/main.cpp Normal file
View File

@@ -0,0 +1,415 @@
#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;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>д<EFBFBD><D0B4> - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŀ<EFBFBD><C4BF><EFBFBD>
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;
}
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;
}
// <20><>ȡĿ<C8A1><C4BF><EFBFBD><EFBFBD><EFBFBD><EFBFBD>DLL<4C><4C>Ϣ
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);
for (Module32First(snap, &mod32); Module32Next(snap, &mod32);)
{
if (mod32.th32ProcessID != pid)
continue;
if (mod32.szModule == ModuleName)
{
*pEntry = mod32;
break;
}
}
CloseHandle(snap);
return pEntry->modBaseAddr;
}
// ͨ<><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, 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);
}
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>
// 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, 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 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> v1.4.2\n");
printf("<EFBFBD><EFBFBD>Ϸ·<EFBFBD><EFBFBD>: %s\n\n", ProcessPath.c_str());
ProcessDir = ProcessPath.substr(0, ProcessPath.find_last_of("\\"));
DWORD pid = GetPID(ProcessPath.substr(ProcessPath.find_last_of("\\") + 1));
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);
// <20>ȴ<EFBFBD>UnityPlayer.dll<6C><6C><EFBFBD>غͻ<D8BA>ȡDLL<4C><4C>Ϣ
MODULEENTRY32 hUnityPlayer{};
MODULEENTRY32 hUserAssembly{};
while (!GetModule(pi.dwProcessId, "UnityPlayer.dll", &hUnityPlayer))
std::this_thread::sleep_for(std::chrono::milliseconds(100));
while (!GetModule(pi.dwProcessId, "UserAssembly.dll", &hUserAssembly))
std::this_thread::sleep_for(std::chrono::milliseconds(100));
printf("UnityPlayer: %X%X\n", (uintptr_t)hUnityPlayer.modBaseAddr >> 32 & -1, hUnityPlayer.modBaseAddr);
printf("UserAssembly: %X%X\n", (uintptr_t)hUnityPlayer.modBaseAddr >> 32 & -1, hUserAssembly.modBaseAddr);
// <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 mem = VirtualAlloc(nullptr, hUnityPlayer.modBaseSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
LPVOID up = VirtualAlloc(nullptr, hUnityPlayer.modBaseSize + hUserAssembly.modBaseSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!up)
{
DWORD code = GetLastError();
printf("VirtualAlloc failed (%d): %s", code, GetLastErrorAsString(code).c_str());
return 0;
}
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ģ<EFBFBD><C4A3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
ReadProcessMemory(pi.hProcess, hUnityPlayer.modBaseAddr, up, hUnityPlayer.modBaseSize, nullptr);
LPVOID ua = (LPVOID)((uintptr_t)up + hUnityPlayer.modBaseSize);
ReadProcessMemory(pi.hProcess, hUserAssembly.modBaseAddr, ua, hUserAssembly.modBaseSize, nullptr);
printf("Searching for pattern...\n");
/*
7F 0F jg 0x11
8B 05 ? ? ? ? mov eax, dword ptr[rip+?]
*/
uintptr_t address = PatternScan(ua, "E8 ? ? ? ? 85 C0 7E 07 E8 ? ? ? ? EB 05");
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 += *(int32_t*)(rip + 1) + 5;
rip += *(int32_t*)(rip + 3) + 7;
uintptr_t ptr = 0;
uintptr_t data = rip - (uintptr_t)ua + (uintptr_t)hUserAssembly.modBaseAddr;
while (!ptr)
{
ReadProcessMemory(pi.hProcess, (LPCVOID)data, &ptr, sizeof(uintptr_t), nullptr);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
rip = ptr - (uintptr_t)hUnityPlayer.modBaseAddr + (uintptr_t)up;
while (*(uint8_t*)rip == 0xE8 || *(uint8_t*)rip == 0xE9)
rip += *(int32_t*)(rip + 1) + 5;
pfps = rip + *(int32_t*)(rip + 2) + 6;
pfps -= (uintptr_t)up;
printf("FPS Offset: %X\n", pfps);
pfps = (uintptr_t)hUnityPlayer.modBaseAddr + pfps;
}
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ե<EFBFBD>ַ (<28><>ֱͬ<D6B1><CDAC>)
address = PatternScan(up, "E8 ? ? ? ? 8B E8 49 8B 1E");
uintptr_t pvsync = 0;
if (address)
{
uintptr_t ppvsync = 0;
uintptr_t rip = address;
int32_t rel = *(int32_t*)(rip + 1);
rip = rip + rel + 5;
uint64_t rax = *(uint32_t*)(rip + 3);
ppvsync = rip + rax + 7;
ppvsync -= (uintptr_t)up;
printf("VSync Offset: %X\n", ppvsync);
ppvsync = (uintptr_t)hUnityPlayer.modBaseAddr + ppvsync;
uintptr_t buffer = 0;
while (!buffer)
{
ReadProcessMemory(pi.hProcess, (LPCVOID)ppvsync, &buffer, sizeof(buffer), nullptr);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
rip += 7;
pvsync = *(uint32_t*)(rip + 2);
pvsync = buffer + pvsync;
}
VirtualFree(up, 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);
int vsync = 0;
ReadProcessMemory(pi.hProcess, (LPVOID)pvsync, &vsync, sizeof(vsync), nullptr);
if (vsync)
{
vsync = 0;
// <20>رմ<D8B1>ֱͬ<D6B1><CDAC>
WriteProcessMemory(pi.hProcess, (LPVOID)pvsync, &vsync, sizeof(vsync), nullptr);
}
}
CloseHandle(pi.hProcess);
TerminateProcess((HANDLE)-1, 0);
return 0;
}