mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
Merge pull request #1816 from DGP-Studio/feat/unlockfps-loadexe
fix #1814
This commit is contained in:
@@ -1,10 +1,6 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.ExceptionService;
|
||||
using Snap.Hutao.Win32.Foundation;
|
||||
using static Snap.Hutao.Win32.Kernel32;
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Unlocker;
|
||||
|
||||
/// <summary>
|
||||
@@ -19,19 +15,34 @@ internal static class GameFpsAddress
|
||||
|
||||
public static unsafe void UnsafeFindFpsAddress(GameFpsUnlockerContext context, in RequiredRemoteModule remoteModule, in RequiredLocalModule localModule)
|
||||
{
|
||||
int offsetToUserAssembly = IndexOfPattern(localModule.UserAssembly.AsSpan());
|
||||
HutaoException.ThrowIfNot(offsetToUserAssembly >= 0, SH.ServiceGameUnlockerInterestedPatternNotFound);
|
||||
Span<byte> executableSpan = localModule.Executable.AsSpan();
|
||||
int offsetToExecutable = 0;
|
||||
nuint localVirtualAddress = 0;
|
||||
do
|
||||
{
|
||||
int index = IndexOfPattern(executableSpan[offsetToExecutable..], out int patternLength);
|
||||
if (index < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
nuint rip = localModule.UserAssembly.Address + (uint)offsetToUserAssembly;
|
||||
rip += 5U;
|
||||
rip += (nuint)(*(int*)(rip + 2U) + 6);
|
||||
offsetToExecutable += index;
|
||||
|
||||
nuint remoteVirtualAddress = remoteModule.UserAssembly.Address + (rip - localModule.UserAssembly.Address);
|
||||
nuint rip = localModule.Executable.Address + (uint)offsetToExecutable;
|
||||
rip += 5U;
|
||||
rip += (nuint)(*(int*)(rip + 1U) + 5);
|
||||
|
||||
nuint ptr = 0;
|
||||
SpinWait.SpinUntil(() => UnsafeReadProcessMemory(context.AllAccess, remoteVirtualAddress, out ptr) && ptr != 0);
|
||||
if (*(byte*)rip is ASM_JMP)
|
||||
{
|
||||
localVirtualAddress = rip;
|
||||
break;
|
||||
}
|
||||
|
||||
nuint localVirtualAddress = ptr - remoteModule.UnityPlayer.Address + localModule.UnityPlayer.Address;
|
||||
offsetToExecutable += patternLength;
|
||||
}
|
||||
while (true);
|
||||
|
||||
ArgumentOutOfRangeException.ThrowIfZero(localVirtualAddress);
|
||||
|
||||
while (*(byte*)localVirtualAddress is ASM_CALL or ASM_JMP)
|
||||
{
|
||||
@@ -39,22 +50,15 @@ internal static class GameFpsAddress
|
||||
}
|
||||
|
||||
localVirtualAddress += *(uint*)(localVirtualAddress + 2) + 6;
|
||||
nuint relativeVirtualAddress = localVirtualAddress - localModule.UnityPlayer.Address;
|
||||
context.FpsAddress = remoteModule.UnityPlayer.Address + relativeVirtualAddress;
|
||||
nuint relativeVirtualAddress = localVirtualAddress - localModule.Executable.Address;
|
||||
context.FpsAddress = remoteModule.Executable.Address + relativeVirtualAddress;
|
||||
}
|
||||
|
||||
private static int IndexOfPattern(in ReadOnlySpan<byte> memory)
|
||||
private static int IndexOfPattern(in ReadOnlySpan<byte> span, out int patternLength)
|
||||
{
|
||||
// B9 3C 00 00 00 FF 15
|
||||
ReadOnlySpan<byte> part = [0xB9, 0x3C, 0x00, 0x00, 0x00, 0xFF, 0x15];
|
||||
return memory.IndexOf(part);
|
||||
}
|
||||
|
||||
private static unsafe bool UnsafeReadProcessMemory(HANDLE hProcess, nuint baseAddress, out nuint value)
|
||||
{
|
||||
value = 0;
|
||||
bool result = ReadProcessMemory(hProcess, (void*)baseAddress, ref value, out _);
|
||||
HutaoException.ThrowIfNot(result, SH.ServiceGameUnlockerReadProcessMemoryPointerAddressFailed);
|
||||
return result;
|
||||
// B9 3C 00 00 00 E8
|
||||
ReadOnlySpan<byte> part = [0xB9, 0x3C, 0x00, 0x00, 0x00, 0xE8];
|
||||
patternLength = part.Length;
|
||||
return span.IndexOf(part);
|
||||
}
|
||||
}
|
||||
@@ -51,12 +51,9 @@ internal abstract class GameFpsUnlocker : IGameFpsUnlocker
|
||||
|
||||
private static RequiredLocalModule LoadRequiredLocalModule(GameFileSystem gameFileSystem)
|
||||
{
|
||||
string gameFoler = gameFileSystem.GameDirectory;
|
||||
string dataFoler = gameFileSystem.DataDirectory;
|
||||
LOAD_LIBRARY_FLAGS flags = LOAD_LIBRARY_FLAGS.LOAD_LIBRARY_AS_IMAGE_RESOURCE;
|
||||
HMODULE unityPlayerAddress = LoadLibraryExW(System.IO.Path.Combine(gameFoler, "UnityPlayer.dll"), default, flags);
|
||||
HMODULE userAssemblyAddress = LoadLibraryExW(System.IO.Path.Combine(dataFoler, "Native", "UserAssembly.dll"), default, flags);
|
||||
HMODULE executaleAddress = LoadLibraryExW(gameFileSystem.GameFilePath, default, flags);
|
||||
|
||||
return new(unityPlayerAddress, userAssemblyAddress);
|
||||
return new(executaleAddress);
|
||||
}
|
||||
}
|
||||
@@ -42,16 +42,15 @@ internal static class GameProcessModule
|
||||
|
||||
private static FindModuleResult UnsafeGetGameModuleInfo(in HANDLE hProcess, out RequiredRemoteModule info)
|
||||
{
|
||||
FindModuleResult unityPlayerResult = UnsafeFindModule(hProcess, "UnityPlayer.dll", out Module unityPlayer);
|
||||
FindModuleResult userAssemblyResult = UnsafeFindModule(hProcess, "UserAssembly.dll", out Module userAssembly);
|
||||
FindModuleResult result = UnsafeFindModule(hProcess, GameConstants.YuanShenFileName, GameConstants.GenshinImpactFileName, out Module executable);
|
||||
|
||||
if (unityPlayerResult is FindModuleResult.Ok && userAssemblyResult is FindModuleResult.Ok)
|
||||
if (result is FindModuleResult.Ok)
|
||||
{
|
||||
info = new(unityPlayer, userAssembly);
|
||||
info = new(executable);
|
||||
return FindModuleResult.Ok;
|
||||
}
|
||||
|
||||
if (unityPlayerResult is FindModuleResult.NoModuleFound && userAssemblyResult is FindModuleResult.NoModuleFound)
|
||||
if (result is FindModuleResult.NoModuleFound)
|
||||
{
|
||||
info = default;
|
||||
return FindModuleResult.NoModuleFound;
|
||||
@@ -61,7 +60,7 @@ internal static class GameProcessModule
|
||||
return FindModuleResult.ModuleNotLoaded;
|
||||
}
|
||||
|
||||
private static unsafe FindModuleResult UnsafeFindModule(in HANDLE hProcess, in ReadOnlySpan<char> moduleName, out Module module)
|
||||
private static unsafe FindModuleResult UnsafeFindModule(in HANDLE hProcess, in ReadOnlySpan<char> moduleName1, in ReadOnlySpan<char> moduleName2, out Module module)
|
||||
{
|
||||
HMODULE[] buffer = new HMODULE[128];
|
||||
if (!K32EnumProcessModules(hProcess, buffer, out uint actualSize))
|
||||
@@ -86,7 +85,8 @@ internal static class GameProcessModule
|
||||
|
||||
fixed (char* lpBaseName = baseName)
|
||||
{
|
||||
if (!moduleName.SequenceEqual(MemoryMarshal.CreateReadOnlySpanFromNullTerminated(lpBaseName)))
|
||||
ReadOnlySpan<char> baseNameSpan = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(lpBaseName);
|
||||
if (!(moduleName1.SequenceEqual(baseNameSpan) || moduleName2.SequenceEqual(baseNameSpan)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -12,30 +12,24 @@ namespace Snap.Hutao.Service.Game.Unlocker;
|
||||
internal readonly struct RequiredLocalModule : IDisposable
|
||||
{
|
||||
public readonly bool HasValue = false;
|
||||
public readonly Module UnityPlayer;
|
||||
public readonly Module UserAssembly;
|
||||
public readonly Module Executable;
|
||||
|
||||
private readonly HMODULE hModuleUnityPlayer;
|
||||
private readonly HMODULE hModuleUserAssembly;
|
||||
private readonly HMODULE hModuleExecutable;
|
||||
|
||||
public RequiredLocalModule(HMODULE unityPlayer, HMODULE userAssembly)
|
||||
public RequiredLocalModule(HMODULE executable)
|
||||
{
|
||||
hModuleUnityPlayer = unityPlayer;
|
||||
hModuleUserAssembly = userAssembly;
|
||||
hModuleExecutable = executable;
|
||||
|
||||
// Align the pointer
|
||||
nint unityPlayerMappedView = (nint)(unityPlayer & ~0x3L);
|
||||
nint userAssemblyMappedView = (nint)(userAssembly & ~0x3L);
|
||||
nint executableMappedView = (nint)(executable & ~0x3L);
|
||||
|
||||
Executable = new((nuint)executableMappedView, GetImageSize(executableMappedView));
|
||||
HasValue = true;
|
||||
UnityPlayer = new((nuint)unityPlayerMappedView, GetImageSize(unityPlayerMappedView));
|
||||
UserAssembly = new((nuint)userAssemblyMappedView, GetImageSize(userAssemblyMappedView));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
FreeLibrary(hModuleUnityPlayer);
|
||||
FreeLibrary(hModuleUserAssembly);
|
||||
FreeLibrary(hModuleExecutable);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
|
||||
@@ -6,13 +6,11 @@ namespace Snap.Hutao.Service.Game.Unlocker;
|
||||
internal readonly struct RequiredRemoteModule
|
||||
{
|
||||
public readonly bool HasValue = false;
|
||||
public readonly Module UnityPlayer;
|
||||
public readonly Module UserAssembly;
|
||||
public readonly Module Executable;
|
||||
|
||||
public RequiredRemoteModule(in Module unityPlayer, in Module userAssembly)
|
||||
public RequiredRemoteModule(in Module executable)
|
||||
{
|
||||
HasValue = true;
|
||||
UnityPlayer = unityPlayer;
|
||||
UserAssembly = userAssembly;
|
||||
Executable = executable;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user