From 34ed3349853790867da9c0a89fae98e58df1deea Mon Sep 17 00:00:00 2001 From: marcussacana Date: Sun, 11 Aug 2024 00:45:09 -0300 Subject: [PATCH] Add FVP Support --- README.MD | 1 + SRLNative/SRLNative.vcxproj | 8 +- StringReloads/AutoInstall/CMVS.cs | 130 +++--------------- StringReloads/AutoInstall/Favorite.cs | 117 ++++++++++++++++ .../Patcher/{Tools.cs => ExeTools.cs} | 2 +- StringReloads/AutoInstall/SoftPalMethodA.cs | 6 +- StringReloads/AutoInstall/SoftPalMethodB.cs | 2 +- StringReloads/Engine/SRL.cs | 3 +- StringReloads/Engine/String/CString.cs | 2 + StringReloads/Engine/String/WCString.cs | 2 + StringReloads/Engine/Unmanaged/ModuleInfo.cs | 2 + StringReloads/Hook/Base/MemoryCodeReader.cs | 1 + StringReloads/Tools/Scanner.cs | 115 ++++++++++++++++ 13 files changed, 271 insertions(+), 120 deletions(-) create mode 100644 StringReloads/AutoInstall/Favorite.cs rename StringReloads/AutoInstall/Patcher/{Tools.cs => ExeTools.cs} (99%) create mode 100644 StringReloads/Tools/Scanner.cs diff --git a/README.MD b/README.MD index 0b2cebd..88ed5d2 100644 --- a/README.MD +++ b/README.MD @@ -92,6 +92,7 @@ First, dowload the SRL [HERE](https://github.com/marcussacana/StringReloads/rele - CMVS32 and CMVS64 - ExHIBIT - EntisGLS +- Favorite (FVP) Some SoftPal games needs a manual setup with the help of the Auto-Installer, click below to see the example: [![SRL SoftPal Auto-Install Feature](http://img.youtube.com/vi/RAgZQBWqiJQ/0.jpg)](http://www.youtube.com/watch?v=RAgZQBWqiJQ "SRL SoftPal Auto-Install Feature") diff --git a/SRLNative/SRLNative.vcxproj b/SRLNative/SRLNative.vcxproj index 8876b7e..876b2b6 100644 --- a/SRLNative/SRLNative.vcxproj +++ b/SRLNative/SRLNative.vcxproj @@ -29,26 +29,26 @@ DynamicLibrary true - v142 + v143 Unicode DynamicLibrary false - v142 + v143 false Unicode DynamicLibrary true - v142 + v143 Unicode DynamicLibrary false - v142 + v143 true Unicode diff --git a/StringReloads/AutoInstall/CMVS.cs b/StringReloads/AutoInstall/CMVS.cs index 72a6c97..a5f818e 100644 --- a/StringReloads/AutoInstall/CMVS.cs +++ b/StringReloads/AutoInstall/CMVS.cs @@ -2,12 +2,14 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net; using Iced.Intel; using StringReloads.Engine; using StringReloads.Engine.Interface; using StringReloads.Engine.Unmanaged; using StringReloads.Hook.Base; using StringReloads.Hook.Others; +using StringReloads.Tools; namespace StringReloads.AutoInstall { @@ -95,51 +97,35 @@ private void EnsureMultiArch() if (!AltSRLAvailable) Log.Warning($"Failed to Find the {AltSRL}."); else - Patcher.Tools.ThirdPartyApplyPatch(AltExe, AltSRL); + Patcher.ExeTools.ThirdPartyApplyPatch(AltExe, AltSRL); - Patcher.Tools.ApplyWrapperPatch(CurrentSRL); + Patcher.ExeTools.ApplyWrapperPatch(CurrentSRL); } void SearchOffset() { - var Imports = ModuleInfo.GetModuleImports((byte*)Config.GameBaseAddress); - var ShellExec = Imports.Where(x => x.Function == "ShellExecuteA").Single(); - - var Bitness = Environment.Is64BitProcess ? 64 : 32; - ulong? Address = null; - byte?[] Pattern = new byte?[] { 0xFF, 0x15 }; - foreach (var lAddress in Scan(Pattern)) + var Bitness = Environment.Is64BitProcess ? 64 : 32; + foreach (var lAddress in Scanner.SearchExportCall("ShellExecuteA")) { - Decoder Dissassembler = Decoder.Create(Bitness, new MemoryCodeReader((byte*)lAddress)); - Dissassembler.IP = (ulong)lAddress; - var Call = Dissassembler.PeekDecode(); - var MemAddress = Call.IPRelativeMemoryAddress; - //if (Environment.Is64BitProcess) - // MemAddress = Call.IPRelativeMemoryAddress + (ulong)lAddress + 6ul; - - if (MemAddress == (ulong)ShellExec.ImportAddress) + var Pattern = new byte?[] { 0xE8 }; + foreach (var lgetTextAddress in Scanner.Scan(Pattern, (ulong)lAddress, true)) { - Log.Debug($"Call dword ptr ds: [&ShellExecuteA] - Found at 0x{lAddress:X16}"); - Pattern = new byte?[] { 0xE8 }; - foreach (var lgetTextAddress in Scan(Pattern, (ulong)lAddress, true)) + var Dissassembler = Decoder.Create(Bitness, new MemoryCodeReader((byte*)lgetTextAddress)); + Dissassembler.IP = (ulong)lgetTextAddress; + var Call = Dissassembler.PeekDecode(); + + var Immediate = Environment.Is64BitProcess ? Call.MemoryDisplacement64 : Call.MemoryDisplacement32; + + if (Address != Immediate) { - Dissassembler = Decoder.Create(Bitness, new MemoryCodeReader((byte*)lgetTextAddress)); - Dissassembler.IP = (ulong)lgetTextAddress; - Call = Dissassembler.PeekDecode(); - - var Immediate = Environment.Is64BitProcess ? Call.MemoryDisplacement64 : Call.MemoryDisplacement32; - - if (Address != Immediate) - { - Address = Immediate; - continue; - } - else - break; + Address = Immediate; + continue; } - break; + else + break; } + break; } if (Address == null) @@ -152,82 +138,6 @@ void SearchOffset() Log.Debug($"CMVS Injection Offset Found: 0x{Offset:X16}"); } - private IEnumerable Scan(byte?[] Pattern, ulong? BeginAddress = null, bool Up = false) - { - ulong? Match = BeginAddress; - do - { - if (Match != null) { - - if (Up) - Match--; - else - Match++; - - } - - Match = Up ? ScanUp(Pattern, Match ?? ulong.MaxValue) : ScanDown(Pattern, Match ?? 0ul); - - if (Match != null) - yield return (long)Match; - - } while (Match != null); - } - - private unsafe ulong? ScanUp(byte?[] Pattern, ulong BeginAddress = ulong.MaxValue) - { - var Info = ModuleInfo.GetCodeInfo((byte*)Config.GameBaseAddress); - - if (BeginAddress == ulong.MaxValue) - BeginAddress = (ulong)Info.CodeAddress + Info.CodeSize; - - ulong CodeAdd = (ulong)Info.CodeAddress; - for (ulong i = BeginAddress - CodeAdd; i >= 0; i--) - { - byte* pAddress = (byte*)(CodeAdd + i); - if (!CheckPattern(pAddress, Pattern)) - continue; - - return (ulong)pAddress; - } - - return null; - } - - private unsafe ulong? ScanDown(byte?[] Pattern, ulong BeginAddress = 0) - { - var Info = ModuleInfo.GetCodeInfo((byte*)Config.GameBaseAddress); - - ulong CodeAdd = (ulong)Info.CodeAddress; - ulong CodeLen = Info.CodeSize; - - if (BeginAddress != 0) - BeginAddress = BeginAddress - CodeAdd; - - for (ulong i = BeginAddress; i < CodeLen; i++) - { - byte* pAddress = (byte*)(CodeAdd + i); - if (!CheckPattern(pAddress, Pattern)) - continue; - - return (ulong)pAddress; - } - - return null; - } - - private bool CheckPattern(byte* Buffer, byte?[] Pattern) - { - for (int i = 0; i < Pattern.Length; i++) - { - if (Pattern[i] == null) - continue; - byte bPattern = Pattern[i].Value; - if (bPattern != Buffer[i]) - return false; - } - return true; - } public bool IsCompatible() => Config.Default.GameExePath.GetFilenameNoExt().ToLowerInvariant().StartsWith("cmvs"); diff --git a/StringReloads/AutoInstall/Favorite.cs b/StringReloads/AutoInstall/Favorite.cs new file mode 100644 index 0000000..47aef84 --- /dev/null +++ b/StringReloads/AutoInstall/Favorite.cs @@ -0,0 +1,117 @@ +using Iced.Intel; +using StringReloads.Engine; +using StringReloads.Engine.Interface; +using StringReloads.Engine.String; +using StringReloads.Engine.Unmanaged; +using StringReloads.Hook; +using StringReloads.Hook.Base; +using StringReloads.Tools; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace StringReloads.AutoInstall +{ + internal class Favorite : IAutoInstall + { + long Address = 0; + Register Register = Register.None; + Intercept Interceptor = null; + + public string Name => "FavoriteViewPoint"; + + public void Install() + { + DetectHookConfig(); + Interceptor.Install(); + } + unsafe void DetectHookConfig() + { + var Settings = Config.Default.GetValues("Favorite"); + + if (Settings == null) + { + foreach (var Import in Scanner.SearchExportCall("lstrlenA")) + { + for (var i = 0; i < 10; i++) + { + var Addr = Import - i; + var Dissassembler = Decoder.Create(32, new MemoryCodeReader((byte*)Addr)); + Dissassembler.IP = (ulong)Addr; + var Instruction = Dissassembler.PeekDecode(); + + switch (Instruction.Code) + { + case Code.Push_r32: + Register = Instruction.Op0Register; + break; + default: + continue; + } + + Address = Import; + break; + } + } + + Settings = new Dictionary(); + Settings["Address"] = Address.ToString(); + Settings["Register"] = ((int)Register).ToString(); + Config.Default.SetValues("Favorite", Settings); + Config.Default.SaveSettings(); + } + else + { + Address = long.Parse(Settings["address"]); + Register = (Register)int.Parse(Settings["register"]); + } + + Interceptor = new ManagedInterceptor((void*)Address, onIntercepted); + Log.Information($"Favorite Engine Intercepted at 0x{Address:X8}"); + } + + private unsafe void onIntercepted(ref ulong ESP, ref ulong EAX, ref ulong ECX, ref ulong EDX, ref ulong EBX, ref ulong EBP, ref ulong ESI, ref ulong EDI) + { + switch (Register) + { + case Register.EBX: + EBX = (ulong)EntryPoint.Process((void*)EBX); + EAX = (ulong)((CString)EBX).Count(); + break; + case Register.ECX: + ECX = (ulong)EntryPoint.Process((void*)ECX); + EAX = (ulong)((CString)ECX).Count(); + break; + case Register.EDX: + EDX = (ulong)EntryPoint.Process((void*)EDX); + EAX = (ulong)((CString)EDX).Count(); + break; + case Register.EBP: + EBP = (ulong)EntryPoint.Process((void*)EBP); + EAX = (ulong)((CString)EBP).Count(); + break; + case Register.ESI: + ESI = (ulong)EntryPoint.Process((void*)ESI); + EAX = (ulong)((CString)ESI).Count(); + break; + case Register.EDI: + EDI = (ulong)EntryPoint.Process((void*)EDI); + EAX = (ulong)((CString)EDI).Count(); + break; + } + } + + public bool IsCompatible() + { + return Directory.GetFiles(".\\", "*.hcb").Any(); + } + + public void Uninstall() + { + Interceptor.Uninstall(); + } + } +} diff --git a/StringReloads/AutoInstall/Patcher/Tools.cs b/StringReloads/AutoInstall/Patcher/ExeTools.cs similarity index 99% rename from StringReloads/AutoInstall/Patcher/Tools.cs rename to StringReloads/AutoInstall/Patcher/ExeTools.cs index dba1252..75e47b6 100644 --- a/StringReloads/AutoInstall/Patcher/Tools.cs +++ b/StringReloads/AutoInstall/Patcher/ExeTools.cs @@ -8,7 +8,7 @@ namespace StringReloads.AutoInstall.Patcher { - static class Tools + static class ExeTools { static readonly string[] SupportedWrappers = new string[] { "version.dll", "d3d9.dll", "d3d10.dll", "d3d11.dll", diff --git a/StringReloads/AutoInstall/SoftPalMethodA.cs b/StringReloads/AutoInstall/SoftPalMethodA.cs index 9da6a6e..153b151 100644 --- a/StringReloads/AutoInstall/SoftPalMethodA.cs +++ b/StringReloads/AutoInstall/SoftPalMethodA.cs @@ -92,7 +92,7 @@ public bool IsCompatible() } } - Tools.ApplyWrapperPatch(); + ExeTools.ApplyWrapperPatch(); SetupMode = true; } @@ -289,11 +289,11 @@ private void FinishSetup() { Config.SaveSettings(); ShowMessageBox($"Perfect! SRL is now ready to use! Enjoy!", "StringReloads", MBButtons.Ok, MBIcon.Information); - Tools.Restart(); + ExeTools.Restart(); } private void SetupFailed() { ShowMessageBox("Hmm, looks like SRL can't perform Auto-Install in this game at the moment. Please report an issue in the GitHub repository.", "StringReloads Setup Wizard", MBButtons.Ok, MBIcon.Error); - Tools.Restart(); + ExeTools.Restart(); } } } diff --git a/StringReloads/AutoInstall/SoftPalMethodB.cs b/StringReloads/AutoInstall/SoftPalMethodB.cs index 46f936e..2163383 100644 --- a/StringReloads/AutoInstall/SoftPalMethodB.cs +++ b/StringReloads/AutoInstall/SoftPalMethodB.cs @@ -35,7 +35,7 @@ public bool IsCompatible() SoftPal_DrawText.AltName = true; } - Tools.ApplyWrapperPatch(); + ExeTools.ApplyWrapperPatch(); Hook = new SoftPal_DrawText(); return true; } diff --git a/StringReloads/Engine/SRL.cs b/StringReloads/Engine/SRL.cs index d4ebbc0..6a9af38 100644 --- a/StringReloads/Engine/SRL.cs +++ b/StringReloads/Engine/SRL.cs @@ -92,7 +92,8 @@ where typeof(IPlugin).IsAssignableFrom(Typ) && !Typ.IsInterface new SoftPalMethodA(), new SoftPalMethodB(), new CMVS(), - new EntisGLS() + new EntisGLS(), + new Favorite() }; internal IEncoding[] Encodings = new IEncoding[0]; diff --git a/StringReloads/Engine/String/CString.cs b/StringReloads/Engine/String/CString.cs index 1981512..e8dfbe2 100644 --- a/StringReloads/Engine/String/CString.cs +++ b/StringReloads/Engine/String/CString.cs @@ -15,6 +15,8 @@ private CString(byte* Ptr) : base(Ptr) { } public Encoding WriteEncoding = Config.Default.WriteEncoding; public static implicit operator CString(byte* Ptr) => new CString(Ptr); + public static implicit operator CString(long Ptr) => new CString((byte*)Ptr); + public static implicit operator CString(ulong Ptr) => new CString((byte*)Ptr); public static implicit operator CString(void* Ptr) => new CString((byte*)Ptr); public static implicit operator CString(IntPtr Ptr) => new CString((byte*)Ptr.ToPointer()); diff --git a/StringReloads/Engine/String/WCString.cs b/StringReloads/Engine/String/WCString.cs index e891d38..b76069d 100644 --- a/StringReloads/Engine/String/WCString.cs +++ b/StringReloads/Engine/String/WCString.cs @@ -12,6 +12,8 @@ public unsafe class WCString : StringBufferW private WCString(byte* Ptr) : base(Ptr) { } public static implicit operator WCString(byte* Ptr) => new WCString(Ptr); + public static implicit operator WCString(long Ptr) => new WCString((byte*)Ptr); + public static implicit operator WCString(ulong Ptr) => new WCString((byte*)Ptr); public static implicit operator WCString(void* Ptr) => new WCString((byte*)Ptr); public static implicit operator WCString(IntPtr Ptr) => new WCString((byte*)Ptr.ToPointer()); diff --git a/StringReloads/Engine/Unmanaged/ModuleInfo.cs b/StringReloads/Engine/Unmanaged/ModuleInfo.cs index e6b5dcd..b0ad423 100644 --- a/StringReloads/Engine/Unmanaged/ModuleInfo.cs +++ b/StringReloads/Engine/Unmanaged/ModuleInfo.cs @@ -23,6 +23,7 @@ public unsafe static CodeInfo GetCodeInfo(byte* hModule) }; } + public unsafe static ImportEntry[] GetMainModuleImports() => GetModuleImports((byte*)Config.GameBaseAddress); public unsafe static ImportEntry[] GetModuleImports(byte* Module) { if (Module == null) @@ -149,6 +150,7 @@ public unsafe struct ImportEntry /// The Address of the Imported Function /// public void* FunctionAddress; + } } \ No newline at end of file diff --git a/StringReloads/Hook/Base/MemoryCodeReader.cs b/StringReloads/Hook/Base/MemoryCodeReader.cs index 4030e1d..cd82508 100644 --- a/StringReloads/Hook/Base/MemoryCodeReader.cs +++ b/StringReloads/Hook/Base/MemoryCodeReader.cs @@ -12,6 +12,7 @@ public unsafe class MemoryCodeReader : CodeReader, IDisposable uint Length = 0; + public MemoryCodeReader(long Address) : this((byte*)Address) { } public MemoryCodeReader(void* Address) : this((byte*)Address) { } public MemoryCodeReader(byte* Address) : this(Address, 0) { } public MemoryCodeReader(void* Address, uint Length) : this((byte*)Address, Length) { } diff --git a/StringReloads/Tools/Scanner.cs b/StringReloads/Tools/Scanner.cs new file mode 100644 index 0000000..5501f0d --- /dev/null +++ b/StringReloads/Tools/Scanner.cs @@ -0,0 +1,115 @@ +using StringReloads.Engine.Unmanaged; +using StringReloads.Engine; +using System; +using System.Collections.Generic; +using System.Linq; +using StringReloads.Hook.Base; +using Iced.Intel; + +namespace StringReloads.Tools +{ + internal unsafe class Scanner + { + public static IEnumerable SearchExportCall(string Function) + { + var Imports = ModuleInfo.GetMainModuleImports(); + var Func = Imports.Where(x => x.Function == Function).Single(); + + var Bitness = Environment.Is64BitProcess ? 64 : 32; + + byte?[] Pattern = new byte?[] { 0xFF, 0x15 }; + foreach (var lAddress in Scan(Pattern)) + { + Decoder Dissassembler = Decoder.Create(Bitness, new MemoryCodeReader(lAddress)); + Dissassembler.IP = (ulong)lAddress; + var Call = Dissassembler.PeekDecode(); + var MemAddress = Call.IPRelativeMemoryAddress; + + if (MemAddress == GetAddress(Func)) + { + Log.Debug($"Call dword ptr ds: [&{Function}] - Found at 0x{lAddress:X16}"); + yield return lAddress; + } + } + } + private unsafe static ulong GetAddress(ImportEntry Entry) => (ulong)Entry.ImportAddress; + + internal static IEnumerable Scan(byte?[] Pattern, ulong? BeginAddress = null, bool Up = false) + { + ulong? Match = BeginAddress; + do + { + if (Match != null) + { + + if (Up) + Match--; + else + Match++; + + } + + Match = Up ? ScanUp(Pattern, Match ?? ulong.MaxValue) : ScanDown(Pattern, Match ?? 0ul); + + if (Match != null) + yield return (long)Match; + + } while (Match != null); + } + + private static ulong? ScanUp(byte?[] Pattern, ulong BeginAddress = ulong.MaxValue) + { + var Info = ModuleInfo.GetCodeInfo((byte*)Config.GameBaseAddress); + + if (BeginAddress == ulong.MaxValue) + BeginAddress = (ulong)Info.CodeAddress + Info.CodeSize; + + ulong CodeAdd = (ulong)Info.CodeAddress; + for (ulong i = BeginAddress - CodeAdd; i >= 0; i--) + { + byte* pAddress = (byte*)(CodeAdd + i); + if (!CheckPattern(pAddress, Pattern)) + continue; + + return (ulong)pAddress; + } + + return null; + } + + private static ulong? ScanDown(byte?[] Pattern, ulong BeginAddress = 0) + { + var Info = ModuleInfo.GetCodeInfo((byte*)Config.GameBaseAddress); + + ulong CodeAdd = (ulong)Info.CodeAddress; + ulong CodeLen = Info.CodeSize; + + if (BeginAddress != 0) + BeginAddress = BeginAddress - CodeAdd; + + for (ulong i = BeginAddress; i < CodeLen; i++) + { + byte* pAddress = (byte*)(CodeAdd + i); + if (!CheckPattern(pAddress, Pattern)) + continue; + + return (ulong)pAddress; + } + + return null; + } + + private static bool CheckPattern(byte* Buffer, byte?[] Pattern) + { + for (int i = 0; i < Pattern.Length; i++) + { + if (Pattern[i] == null) + continue; + byte bPattern = Pattern[i].Value; + if (bPattern != Buffer[i]) + return false; + } + return true; + } + } +}