From d02a699f594abb0a359c1523bb1869ec9756f52e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8D=A3=E8=80=80=E7=9A=84=E6=8D=8D=E5=8D=AB=E8=80=85?= <1250839773@qq.com> Date: Thu, 27 Jun 2024 13:44:52 +0800 Subject: [PATCH] add hotpatch --- README.md | 2 + unlockfps/main.cpp | 312 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 313 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2272983..397191f 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ ver 4.7 **若您在B服的解锁出现了问题,请下载winTEuser老哥的版本 ([点我进入](https://github.com/winTEuser/Genshin_StarRail_fps_unlocker/releases)),如果解决的您的问题请不要吝啬您的star** +**如果没有什么意外的话,本项目将跟随 winTEuser老哥 版本的代码** + ver 4.7 特别感谢:**winTEuser** 老哥支持,让我受益良多 diff --git a/unlockfps/main.cpp b/unlockfps/main.cpp index 77df06e..f2fbe66 100644 --- a/unlockfps/main.cpp +++ b/unlockfps/main.cpp @@ -25,6 +25,64 @@ const std::vector PrioityClass = { BELOW_NORMAL_PRIORITY_CLASS, IDLE_PRIORITY_CLASS }; +//credit by winTEuser +BYTE _shellcode_genshin[] = +{ + 0x00, 0x00, 0x00, 0x00, // DWORD unlocker_pid _shellcode_genshin[0] + 0x00, 0x00, 0x00, 0x00, // DWORD unlocker_Handle _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, //uint32_t Readmem_buffer _shellcode_genshin[40] + 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 + 0x74, 0x49, //je return + 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 + 0x74, 0x35, //je return + 0x89, 0x05, 0xAC, 0xFF, 0xFF, 0xFF, //mov dword[unlocker_Handle],eax + 0x89, 0xC6, //mov esi,eax + 0x48, 0x8B, 0x3D, 0xA7, 0xFF, 0xFF, 0xFF, //mov rdi,qword[unlocker_FpsValue_addr] + 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 + 0x75, 0x06, //jne sleep + 0x48, 0x83, 0xC4, 0x38, //add rsp,0x38 //return + 0xC3, //ret + 0xCC, //int3 + 0xB9, 0xE8, 0x03, 0x00, 0x00, //mov ecx,0x3e8 //(1000ms) sleep + 0xFF, 0x15, 0x88, 0xFF, 0xFF, 0xFF, //call [API_Sleep] + 0xEB, 0xCA, //jmp Read_tar_fps + 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, //int3 + 0x83, 0xF9, 0x1E, //cmp ecx,0x1E //hook_fps_set _shellcode_genshin[160] + 0x74, 0x14, //je set 60 + 0x83, 0xF9, 0x2D, //cmp ecx,0x2D + 0x74, 0x07, //je set tar_fps + 0xB9, 0xFF, 0xFF, 0xFF, 0xFF, //mov ecx, -1 //default set unlimited + 0xEB, 0x0D, //jmp set + 0x8B, 0x0D, 0x71, 0xFF, 0xFF, 0xFF, //mov ecx,[Readmem_buffer] + 0xEB, 0x05, //jmp set + 0xB9, 0x3C, 0x00, 0x00, 0x00, //mov ecx,0x3C + 0x89, 0x0D, 0x0D, 0x00, 0x00, 0x00, //mov [hook_set],ecx + 0xC3, //ret + 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, //int3 + 0xB8,0x78, 0x00, 0x00, 0x00, //mov eax,0x78 //hook_fps_get _shellcode_genshin[208] + 0xC3, //ret + 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC //int3 +}; + // - д - Ŀ uintptr_t PatternScan(void* module, const char* signature) { @@ -71,6 +129,46 @@ uintptr_t PatternScan(void* module, const char* signature) } return 0; } +static uintptr_t PatternScan_Region(uintptr_t startAddress, size_t regionSize, const char* signature) +{ + auto pattern_to_byte = [](const char* pattern) + { + std::vector 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(¤t), 16)); + } + } + return bytes; + }; + + std::vector patternBytes = pattern_to_byte(signature); + auto scanBytes = reinterpret_cast(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) { @@ -152,6 +250,209 @@ bool WriteConfig(std::string GamePath, int fps) WriteFile(hFile, content.data(), content.size(), &written, nullptr); CloseHandle(hFile); } +//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;//ָڶβ + DWORD num = sec_num; + while (num) + { + _sec_temp = *(_IMAGE_SECTION_HEADER*)(PEfptr + 264 + (40 * (static_cast(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"))//ȷpatchλ//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"))//ȷpatchλ//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; + } + } +} void LoadConfig() { @@ -300,7 +601,7 @@ int main(int argc, char** argv) CloseHandle(pi.hThread); printf("PID: %d\n", pi.dwProcessId); - StartPriority = PrioityClass[3]; + StartPriority = PrioityClass[1]; SetPriorityClass(pi.hProcess, StartPriority); // ȴUnityPlayer.dllغͻȡDLLϢ @@ -350,6 +651,14 @@ int main(int argc, char** argv) pfps = rip - (uintptr_t)up + (uintptr_t)hUnityPlayer.modBaseAddr; printf("FPS Offset: %X\n", pfps); } + 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"); + } + } VirtualFree(up, 0, MEM_RELEASE); printf("Done\n\n"); @@ -378,6 +687,7 @@ int main(int argc, char** argv) if (fps != TargetFPS) WriteProcessMemory(pi.hProcess, (LPVOID)pfps, &TargetFPS, sizeof(TargetFPS), nullptr); + } CloseHandle(pi.hProcess);