mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 18:27:23 +01:00
First resource manager hooks
This commit is contained in:
parent
ac838687f8
commit
5c7c6fc7d6
5 changed files with 256 additions and 1 deletions
|
|
@ -22,7 +22,20 @@ namespace Dalamud.Injector {
|
||||||
int pid = int.Parse(args[0]);
|
int pid = int.Parse(args[0]);
|
||||||
|
|
||||||
Process process = null;
|
Process process = null;
|
||||||
process = pid == -1 ? Process.GetProcessesByName("ffxiv_dx11")[0] : Process.GetProcessById(pid);
|
|
||||||
|
switch (pid) {
|
||||||
|
case -1:
|
||||||
|
process = Process.GetProcessesByName("ffxiv_dx11")[0];
|
||||||
|
break;
|
||||||
|
case -2:
|
||||||
|
process = Process.Start(
|
||||||
|
"C:\\Program Files (x86)\\SquareEnix\\FINAL FANTASY XIV - A Realm Reborn\\game\\ffxiv_dx11.exe",
|
||||||
|
"DEV.TestSID=5fa077c389a61c4a45ea35153162753d7cdb34268cc38c9e206859a7 DEV.UseSqPack=1 DEV.DataPathType=1 DEV.LobbyHost01=127.0.0.1 DEV.LobbyPort01=54994 DEV.LobbyHost02=127.0.0.1 DEV.LobbyPort02=54994 DEV.LobbyHost03=127.0.0.1 DEV.LobbyPort03=54994 DEV.LobbyHost04=127.0.0.1 DEV.LobbyPort04=54994 DEV.LobbyHost05=127.0.0.1 DEV.LobbyPort05=54994 DEV.LobbyHost06=127.0.0.1 DEV.LobbyPort06=54994 DEV.LobbyHost07=127.0.0.1 DEV.LobbyPort07=54994 DEV.LobbyHost08=127.0.0.1 DEV.LobbyPort08=54994 SYS.Region=0 language=1 version=1.0.0.0 DEV.MaxEntitledExpansionID=2 DEV.GMServerHost=127.0.0.1 DEV.GameQuitMessageBox=0");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
process = Process.GetProcessById(pid);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
var startInfo = JsonConvert.DeserializeObject<DalamudStartInfo>(Encoding.UTF8.GetString(Convert.FromBase64String(args[1])));
|
var startInfo = JsonConvert.DeserializeObject<DalamudStartInfo>(Encoding.UTF8.GetString(Convert.FromBase64String(args[1])));
|
||||||
startInfo.WorkingDirectory = Directory.GetCurrentDirectory();
|
startInfo.WorkingDirectory = Directory.GetCurrentDirectory();
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,11 @@ namespace Dalamud {
|
||||||
|
|
||||||
public readonly ClientState ClientState;
|
public readonly ClientState ClientState;
|
||||||
|
|
||||||
|
public readonly DalamudStartInfo StartInfo;
|
||||||
|
|
||||||
public Dalamud(DalamudStartInfo info) {
|
public Dalamud(DalamudStartInfo info) {
|
||||||
|
this.StartInfo = info;
|
||||||
|
|
||||||
this.baseDirectory = info.WorkingDirectory;
|
this.baseDirectory = info.WorkingDirectory;
|
||||||
|
|
||||||
this.unloadSignal = new ManualResetEvent(false);
|
this.unloadSignal = new ManualResetEvent(false);
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ using Dalamud.Game.Internal.Libc;
|
||||||
using Dalamud.Game.Internal.Network;
|
using Dalamud.Game.Internal.Network;
|
||||||
using Dalamud.Hooking;
|
using Dalamud.Hooking;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
using Dalamud.Game.Internal.File;
|
||||||
|
|
||||||
namespace Dalamud.Game.Internal {
|
namespace Dalamud.Game.Internal {
|
||||||
public sealed class Framework : IDisposable {
|
public sealed class Framework : IDisposable {
|
||||||
|
|
@ -29,6 +30,8 @@ namespace Dalamud.Game.Internal {
|
||||||
public GameGui Gui { get; private set; }
|
public GameGui Gui { get; private set; }
|
||||||
|
|
||||||
public GameNetwork Network { get; private set; }
|
public GameNetwork Network { get; private set; }
|
||||||
|
|
||||||
|
public ResourceManager Resource { get; private set; }
|
||||||
|
|
||||||
public LibcFunction Libc { get; private set; }
|
public LibcFunction Libc { get; private set; }
|
||||||
|
|
||||||
|
|
@ -52,6 +55,8 @@ namespace Dalamud.Game.Internal {
|
||||||
Gui = new GameGui(Address.GuiManager, scanner, dalamud);
|
Gui = new GameGui(Address.GuiManager, scanner, dalamud);
|
||||||
|
|
||||||
Network = new GameNetwork(dalamud, scanner);
|
Network = new GameNetwork(dalamud, scanner);
|
||||||
|
|
||||||
|
Resource = new ResourceManager(dalamud, scanner);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HookVTable() {
|
private void HookVTable() {
|
||||||
|
|
@ -70,6 +75,7 @@ namespace Dalamud.Game.Internal {
|
||||||
public void Enable() {
|
public void Enable() {
|
||||||
Gui.Enable();
|
Gui.Enable();
|
||||||
Network.Enable();
|
Network.Enable();
|
||||||
|
//Resource.Enable();
|
||||||
|
|
||||||
this.updateHook.Enable();
|
this.updateHook.Enable();
|
||||||
}
|
}
|
||||||
|
|
@ -77,6 +83,7 @@ namespace Dalamud.Game.Internal {
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
Gui.Dispose();
|
Gui.Dispose();
|
||||||
Network.Dispose();
|
Network.Dispose();
|
||||||
|
//Resource.Dispose();
|
||||||
|
|
||||||
this.updateHook.Dispose();
|
this.updateHook.Dispose();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
211
Dalamud/Game/Internal/Resource/ResourceManager.cs
Normal file
211
Dalamud/Game/Internal/Resource/ResourceManager.cs
Normal file
|
|
@ -0,0 +1,211 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Dalamud.Game.Internal.Libc;
|
||||||
|
using Dalamud.Hooking;
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
|
namespace Dalamud.Game.Internal.File
|
||||||
|
{
|
||||||
|
public class ResourceManager {
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
|
||||||
|
private delegate IntPtr GetResourceAsyncDelegate(IntPtr manager, IntPtr a2, IntPtr a3, IntPtr a4, IntPtr a5, IntPtr a6, byte a7);
|
||||||
|
private readonly Hook<GetResourceAsyncDelegate> getResourceAsyncHook;
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
|
||||||
|
private delegate IntPtr GetResourceSyncDelegate(IntPtr manager, IntPtr a2, IntPtr a3, IntPtr a4, IntPtr a5, IntPtr a6);
|
||||||
|
private readonly Hook<GetResourceSyncDelegate> getResourceSyncHook;
|
||||||
|
|
||||||
|
private ResourceManagerAddressResolver Address { get; }
|
||||||
|
private readonly Dalamud dalamud;
|
||||||
|
|
||||||
|
class ResourceHandleHookInfo {
|
||||||
|
public string Path { get; set; }
|
||||||
|
public Stream DetourFile { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dictionary<IntPtr, ResourceHandleHookInfo> resourceHookMap = new Dictionary<IntPtr, ResourceHandleHookInfo>();
|
||||||
|
|
||||||
|
public ResourceManager(Dalamud dalamud, SigScanner scanner) {
|
||||||
|
this.dalamud = dalamud;
|
||||||
|
Address = new ResourceManagerAddressResolver();
|
||||||
|
Address.Setup(scanner);
|
||||||
|
|
||||||
|
Log.Verbose("===== R E S O U R C E M A N A G E R =====");
|
||||||
|
Log.Verbose("GetResourceAsync address {GetResourceAsync}", Address.GetResourceAsync);
|
||||||
|
Log.Verbose("GetResourceSync address {GetResourceSync}", Address.GetResourceSync);
|
||||||
|
|
||||||
|
this.getResourceAsyncHook =
|
||||||
|
new Hook<GetResourceAsyncDelegate>(Address.GetResourceAsync,
|
||||||
|
new GetResourceAsyncDelegate(GetResourceAsyncDetour),
|
||||||
|
this);
|
||||||
|
|
||||||
|
this.getResourceSyncHook =
|
||||||
|
new Hook<GetResourceSyncDelegate>(Address.GetResourceSync,
|
||||||
|
new GetResourceSyncDelegate(GetResourceSyncDetour),
|
||||||
|
this);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Enable() {
|
||||||
|
this.getResourceAsyncHook.Enable();
|
||||||
|
this.getResourceSyncHook.Enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() {
|
||||||
|
this.getResourceAsyncHook.Dispose();
|
||||||
|
this.getResourceSyncHook.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private IntPtr GetResourceAsyncDetour(IntPtr manager, IntPtr a2, IntPtr a3, IntPtr a4, IntPtr a5, IntPtr a6, byte a7) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
var path = Marshal.PtrToStringAnsi(a5);
|
||||||
|
|
||||||
|
var resourceHandle = this.getResourceAsyncHook.Original(manager, a2, a3, a4, IntPtr.Zero, a6, a7);
|
||||||
|
//var resourceHandle = IntPtr.Zero;
|
||||||
|
|
||||||
|
Log.Verbose("GetResourceAsync CALL - this:{0} a2:{1} a3:{2} a4:{3} a5:{4} a6:{5} a7:{6} => RET:{7}", manager, a2, a3, a4, a5, a6, a7, resourceHandle);
|
||||||
|
|
||||||
|
Log.Verbose($"->{path}");
|
||||||
|
|
||||||
|
HandleGetResourceHookAcquire(resourceHandle, path);
|
||||||
|
|
||||||
|
return resourceHandle;
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Log.Error(ex, "Exception on ReadResourceAsync hook.");
|
||||||
|
|
||||||
|
return this.getResourceAsyncHook.Original(manager, a2, a3, a4, a5, a6, a7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DumpMem(IntPtr address, int len = 512) {
|
||||||
|
if (address == IntPtr.Zero)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var data = new byte[len];
|
||||||
|
Marshal.Copy(address, data, 0, len);
|
||||||
|
|
||||||
|
Log.Verbose($"MEMDMP at {address.ToInt64():X} for {len:X}\n{ByteArrayToHex(data)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private IntPtr GetResourceSyncDetour(IntPtr manager, IntPtr a2, IntPtr a3, IntPtr a4, IntPtr a5, IntPtr a6) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
var resourceHandle = this.getResourceSyncHook.Original(manager, a2, a3, a4, a5, a6);
|
||||||
|
|
||||||
|
Log.Verbose("GetResourceSync CALL - this:{0} a2:{1} a3:{2} a4:{3} a5:{4} a6:{5} => RET:{6}", manager, a2, a3, a4, a5, a6, resourceHandle);
|
||||||
|
|
||||||
|
var path = Marshal.PtrToStringAnsi(a5);
|
||||||
|
|
||||||
|
Log.Verbose($"->{path}");
|
||||||
|
|
||||||
|
HandleGetResourceHookAcquire(resourceHandle, path);
|
||||||
|
|
||||||
|
return resourceHandle;
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Log.Error(ex, "Exception on ReadResourceSync hook.");
|
||||||
|
|
||||||
|
return this.getResourceSyncHook.Original(manager, a2, a3, a4, a5, a6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleGetResourceHookAcquire(IntPtr handlePtr, string path) {
|
||||||
|
if (FilePathHasInvalidChars(path))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (this.resourceHookMap.ContainsKey(handlePtr)) {
|
||||||
|
Log.Verbose($"-> Handle {handlePtr.ToInt64():X}({path}) was cached!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var hookInfo = new ResourceHandleHookInfo {
|
||||||
|
Path = path
|
||||||
|
};
|
||||||
|
|
||||||
|
var hookPath = Path.Combine(this.dalamud.StartInfo.WorkingDirectory, "ResourceHook", path);
|
||||||
|
|
||||||
|
if (System.IO.File.Exists(hookPath)) {
|
||||||
|
hookInfo.DetourFile = new FileStream(hookPath, FileMode.Open);
|
||||||
|
Log.Verbose("-> Added resource hook detour at {0}", hookPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.resourceHookMap.Add(handlePtr, hookInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool FilePathHasInvalidChars(string path)
|
||||||
|
{
|
||||||
|
|
||||||
|
return (!string.IsNullOrEmpty(path) && path.IndexOfAny(System.IO.Path.GetInvalidPathChars()) >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ByteArrayToHex(byte[] bytes, int offset = 0, int bytesPerLine = 16)
|
||||||
|
{
|
||||||
|
if (bytes == null)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
var hexChars = "0123456789ABCDEF".ToCharArray();
|
||||||
|
|
||||||
|
var offsetBlock = 8 + 3;
|
||||||
|
var byteBlock = offsetBlock + bytesPerLine * 3 + (bytesPerLine - 1) / 8 + 2;
|
||||||
|
var lineLength = byteBlock + bytesPerLine + Environment.NewLine.Length;
|
||||||
|
|
||||||
|
var line = (new string(' ', lineLength - Environment.NewLine.Length) + Environment.NewLine).ToCharArray();
|
||||||
|
var numLines = (bytes.Length + bytesPerLine - 1) / bytesPerLine;
|
||||||
|
|
||||||
|
var sb = new StringBuilder(numLines * lineLength);
|
||||||
|
|
||||||
|
for (var i = 0; i < bytes.Length; i += bytesPerLine)
|
||||||
|
{
|
||||||
|
var h = i + offset;
|
||||||
|
|
||||||
|
line[0] = hexChars[(h >> 28) & 0xF];
|
||||||
|
line[1] = hexChars[(h >> 24) & 0xF];
|
||||||
|
line[2] = hexChars[(h >> 20) & 0xF];
|
||||||
|
line[3] = hexChars[(h >> 16) & 0xF];
|
||||||
|
line[4] = hexChars[(h >> 12) & 0xF];
|
||||||
|
line[5] = hexChars[(h >> 8) & 0xF];
|
||||||
|
line[6] = hexChars[(h >> 4) & 0xF];
|
||||||
|
line[7] = hexChars[(h >> 0) & 0xF];
|
||||||
|
|
||||||
|
var hexColumn = offsetBlock;
|
||||||
|
var charColumn = byteBlock;
|
||||||
|
|
||||||
|
for (var j = 0; j < bytesPerLine; j++)
|
||||||
|
{
|
||||||
|
if (j > 0 && (j & 7) == 0)
|
||||||
|
{
|
||||||
|
hexColumn++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i + j >= bytes.Length)
|
||||||
|
{
|
||||||
|
line[hexColumn] = ' ';
|
||||||
|
line[hexColumn + 1] = ' ';
|
||||||
|
line[charColumn] = ' ';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var by = bytes[i + j];
|
||||||
|
line[hexColumn] = hexChars[(by >> 4) & 0xF];
|
||||||
|
line[hexColumn + 1] = hexChars[by & 0xF];
|
||||||
|
line[charColumn] = by < 32 ? '.' : (char)by;
|
||||||
|
}
|
||||||
|
|
||||||
|
hexColumn += 3;
|
||||||
|
charColumn++;
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.Append(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString().TrimEnd(Environment.NewLine.ToCharArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Dalamud.Game.Internal.File
|
||||||
|
{
|
||||||
|
class ResourceManagerAddressResolver : BaseAddressResolver
|
||||||
|
{
|
||||||
|
public IntPtr GetResourceAsync { get; private set; }
|
||||||
|
public IntPtr GetResourceSync { get; private set; }
|
||||||
|
|
||||||
|
protected override void Setup64Bit(SigScanner sig) {
|
||||||
|
GetResourceAsync = sig.ScanText("48 89 5C 24 08 48 89 54 24 10 57 48 83 EC 20 B8 03 00 00 00 48 8B F9 86 82 A1 00 00 00 48 8B 5C 24 38 B8 01 00 00 00 87 83 90 00 00 00 85 C0 74");
|
||||||
|
GetResourceSync = sig.ScanText("48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 41 54 41 55 41 56 41 57 48 83 EC 30 48 8B F9 49 8B E9 48 83 C1 30 4D 8B F0 4C 8B EA FF 15 CE F6");
|
||||||
|
//ReadResourceSync = sig.ScanText("48 89 74 24 18 57 48 83 EC 50 8B F2 49 8B F8 41 0F B7 50 02 8B CE E8 ?? ?? 7A FF 0F B7 57 02 8D 42 89 3D 5F 02 00 00 0F 87 60 01 00 00 4C 8D 05");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue