Merge pull request #1816 from DGP-Studio/feat/unlockfps-loadexe

fix #1814
This commit is contained in:
DismissedLight
2024-07-17 11:18:26 +08:00
committed by GitHub
5 changed files with 50 additions and 57 deletions

View File

@@ -1,10 +1,6 @@
// Copyright (c) DGP Studio. All rights reserved. // Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license. // 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; namespace Snap.Hutao.Service.Game.Unlocker;
/// <summary> /// <summary>
@@ -19,19 +15,34 @@ internal static class GameFpsAddress
public static unsafe void UnsafeFindFpsAddress(GameFpsUnlockerContext context, in RequiredRemoteModule remoteModule, in RequiredLocalModule localModule) public static unsafe void UnsafeFindFpsAddress(GameFpsUnlockerContext context, in RequiredRemoteModule remoteModule, in RequiredLocalModule localModule)
{ {
int offsetToUserAssembly = IndexOfPattern(localModule.UserAssembly.AsSpan()); Span<byte> executableSpan = localModule.Executable.AsSpan();
HutaoException.ThrowIfNot(offsetToUserAssembly >= 0, SH.ServiceGameUnlockerInterestedPatternNotFound); 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; offsetToExecutable += index;
rip += 5U;
rip += (nuint)(*(int*)(rip + 2U) + 6);
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; if (*(byte*)rip is ASM_JMP)
SpinWait.SpinUntil(() => UnsafeReadProcessMemory(context.AllAccess, remoteVirtualAddress, out ptr) && ptr != 0); {
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) while (*(byte*)localVirtualAddress is ASM_CALL or ASM_JMP)
{ {
@@ -39,22 +50,15 @@ internal static class GameFpsAddress
} }
localVirtualAddress += *(uint*)(localVirtualAddress + 2) + 6; localVirtualAddress += *(uint*)(localVirtualAddress + 2) + 6;
nuint relativeVirtualAddress = localVirtualAddress - localModule.UnityPlayer.Address; nuint relativeVirtualAddress = localVirtualAddress - localModule.Executable.Address;
context.FpsAddress = remoteModule.UnityPlayer.Address + relativeVirtualAddress; 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 // B9 3C 00 00 00 E8
ReadOnlySpan<byte> part = [0xB9, 0x3C, 0x00, 0x00, 0x00, 0xFF, 0x15]; ReadOnlySpan<byte> part = [0xB9, 0x3C, 0x00, 0x00, 0x00, 0xE8];
return memory.IndexOf(part); patternLength = part.Length;
} return span.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;
} }
} }

View File

@@ -51,12 +51,9 @@ internal abstract class GameFpsUnlocker : IGameFpsUnlocker
private static RequiredLocalModule LoadRequiredLocalModule(GameFileSystem gameFileSystem) 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; 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 executaleAddress = LoadLibraryExW(gameFileSystem.GameFilePath, default, flags);
HMODULE userAssemblyAddress = LoadLibraryExW(System.IO.Path.Combine(dataFoler, "Native", "UserAssembly.dll"), default, flags);
return new(unityPlayerAddress, userAssemblyAddress); return new(executaleAddress);
} }
} }

View File

@@ -42,16 +42,15 @@ internal static class GameProcessModule
private static FindModuleResult UnsafeGetGameModuleInfo(in HANDLE hProcess, out RequiredRemoteModule info) private static FindModuleResult UnsafeGetGameModuleInfo(in HANDLE hProcess, out RequiredRemoteModule info)
{ {
FindModuleResult unityPlayerResult = UnsafeFindModule(hProcess, "UnityPlayer.dll", out Module unityPlayer); FindModuleResult result = UnsafeFindModule(hProcess, GameConstants.YuanShenFileName, GameConstants.GenshinImpactFileName, out Module executable);
FindModuleResult userAssemblyResult = UnsafeFindModule(hProcess, "UserAssembly.dll", out Module userAssembly);
if (unityPlayerResult is FindModuleResult.Ok && userAssemblyResult is FindModuleResult.Ok) if (result is FindModuleResult.Ok)
{ {
info = new(unityPlayer, userAssembly); info = new(executable);
return FindModuleResult.Ok; return FindModuleResult.Ok;
} }
if (unityPlayerResult is FindModuleResult.NoModuleFound && userAssemblyResult is FindModuleResult.NoModuleFound) if (result is FindModuleResult.NoModuleFound)
{ {
info = default; info = default;
return FindModuleResult.NoModuleFound; return FindModuleResult.NoModuleFound;
@@ -61,7 +60,7 @@ internal static class GameProcessModule
return FindModuleResult.ModuleNotLoaded; 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]; HMODULE[] buffer = new HMODULE[128];
if (!K32EnumProcessModules(hProcess, buffer, out uint actualSize)) if (!K32EnumProcessModules(hProcess, buffer, out uint actualSize))
@@ -86,7 +85,8 @@ internal static class GameProcessModule
fixed (char* lpBaseName = baseName) fixed (char* lpBaseName = baseName)
{ {
if (!moduleName.SequenceEqual(MemoryMarshal.CreateReadOnlySpanFromNullTerminated(lpBaseName))) ReadOnlySpan<char> baseNameSpan = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(lpBaseName);
if (!(moduleName1.SequenceEqual(baseNameSpan) || moduleName2.SequenceEqual(baseNameSpan)))
{ {
continue; continue;
} }

View File

@@ -12,30 +12,24 @@ namespace Snap.Hutao.Service.Game.Unlocker;
internal readonly struct RequiredLocalModule : IDisposable internal readonly struct RequiredLocalModule : IDisposable
{ {
public readonly bool HasValue = false; public readonly bool HasValue = false;
public readonly Module UnityPlayer; public readonly Module Executable;
public readonly Module UserAssembly;
private readonly HMODULE hModuleUnityPlayer; private readonly HMODULE hModuleExecutable;
private readonly HMODULE hModuleUserAssembly;
public RequiredLocalModule(HMODULE unityPlayer, HMODULE userAssembly) public RequiredLocalModule(HMODULE executable)
{ {
hModuleUnityPlayer = unityPlayer; hModuleExecutable = executable;
hModuleUserAssembly = userAssembly;
// Align the pointer // Align the pointer
nint unityPlayerMappedView = (nint)(unityPlayer & ~0x3L); nint executableMappedView = (nint)(executable & ~0x3L);
nint userAssemblyMappedView = (nint)(userAssembly & ~0x3L);
Executable = new((nuint)executableMappedView, GetImageSize(executableMappedView));
HasValue = true; HasValue = true;
UnityPlayer = new((nuint)unityPlayerMappedView, GetImageSize(unityPlayerMappedView));
UserAssembly = new((nuint)userAssemblyMappedView, GetImageSize(userAssemblyMappedView));
} }
public void Dispose() public void Dispose()
{ {
FreeLibrary(hModuleUnityPlayer); FreeLibrary(hModuleExecutable);
FreeLibrary(hModuleUserAssembly);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]

View File

@@ -6,13 +6,11 @@ namespace Snap.Hutao.Service.Game.Unlocker;
internal readonly struct RequiredRemoteModule internal readonly struct RequiredRemoteModule
{ {
public readonly bool HasValue = false; public readonly bool HasValue = false;
public readonly Module UnityPlayer; public readonly Module Executable;
public readonly Module UserAssembly;
public RequiredRemoteModule(in Module unityPlayer, in Module userAssembly) public RequiredRemoteModule(in Module executable)
{ {
HasValue = true; HasValue = true;
UnityPlayer = unityPlayer; Executable = executable;
UserAssembly = userAssembly;
} }
} }