First resource manager hooks

This commit is contained in:
goat 2019-10-15 20:55:03 +09:00
parent ac838687f8
commit 5c7c6fc7d6
5 changed files with 256 additions and 1 deletions

View file

@ -22,7 +22,20 @@ namespace Dalamud.Injector {
int pid = int.Parse(args[0]);
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])));
startInfo.WorkingDirectory = Directory.GetCurrentDirectory();

View file

@ -37,7 +37,11 @@ namespace Dalamud {
public readonly ClientState ClientState;
public readonly DalamudStartInfo StartInfo;
public Dalamud(DalamudStartInfo info) {
this.StartInfo = info;
this.baseDirectory = info.WorkingDirectory;
this.unloadSignal = new ManualResetEvent(false);

View file

@ -6,6 +6,7 @@ using Dalamud.Game.Internal.Libc;
using Dalamud.Game.Internal.Network;
using Dalamud.Hooking;
using Serilog;
using Dalamud.Game.Internal.File;
namespace Dalamud.Game.Internal {
public sealed class Framework : IDisposable {
@ -29,6 +30,8 @@ namespace Dalamud.Game.Internal {
public GameGui Gui { get; private set; }
public GameNetwork Network { get; private set; }
public ResourceManager Resource { get; private set; }
public LibcFunction Libc { get; private set; }
@ -52,6 +55,8 @@ namespace Dalamud.Game.Internal {
Gui = new GameGui(Address.GuiManager, scanner, dalamud);
Network = new GameNetwork(dalamud, scanner);
Resource = new ResourceManager(dalamud, scanner);
}
private void HookVTable() {
@ -70,6 +75,7 @@ namespace Dalamud.Game.Internal {
public void Enable() {
Gui.Enable();
Network.Enable();
//Resource.Enable();
this.updateHook.Enable();
}
@ -77,6 +83,7 @@ namespace Dalamud.Game.Internal {
public void Dispose() {
Gui.Dispose();
Network.Dispose();
//Resource.Dispose();
this.updateHook.Dispose();
}

View 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());
}
}
}

View file

@ -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");
}
}
}