mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 10:17:22 +01:00
Implement xiv fixes into Dalamud.Boot (#857)
This commit is contained in:
parent
02dd1eddec
commit
75de126c9d
40 changed files with 41576 additions and 196 deletions
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
|
@ -324,7 +325,7 @@ namespace Dalamud.Injector
|
|||
Console.WriteLine("{0} help [command]", exeName);
|
||||
|
||||
if (particularCommand is null or "inject")
|
||||
Console.WriteLine("{0} inject [-h/--help] [-a/--all] [--warn] [pid1] [pid2] [pid3] ...", exeName);
|
||||
Console.WriteLine("{0} inject [-h/--help] [-a/--all] [--warn] [--fix-acl] [--se-debug-privilege] [pid1] [pid2] [pid3] ...", exeName);
|
||||
|
||||
if (particularCommand is null or "launch")
|
||||
{
|
||||
|
|
@ -332,7 +333,7 @@ namespace Dalamud.Injector
|
|||
Console.WriteLine("{0} [-g path/to/ffxiv_dx11.exe] [--game=path/to/ffxiv_dx11.exe]", exeSpaces);
|
||||
Console.WriteLine("{0} [-m entrypoint|inject] [--mode=entrypoint|inject]", exeSpaces);
|
||||
Console.WriteLine("{0} [--handle-owner=inherited-handle-value]", exeSpaces);
|
||||
Console.WriteLine("{0} [--without-dalamud]", exeSpaces);
|
||||
Console.WriteLine("{0} [--without-dalamud] [--no-fix-acl]", exeSpaces);
|
||||
Console.WriteLine("{0} [-- game_arg1=value1 game_arg2=value2 ...]", exeSpaces);
|
||||
}
|
||||
|
||||
|
|
@ -351,6 +352,8 @@ namespace Dalamud.Injector
|
|||
var targetProcessSpecified = false;
|
||||
var warnManualInjection = false;
|
||||
var showHelp = args.Count <= 2;
|
||||
var tryFixAcl = false;
|
||||
var tryClaimSeDebugPrivilege = false;
|
||||
|
||||
for (var i = 2; i < args.Count; i++)
|
||||
{
|
||||
|
|
@ -378,6 +381,14 @@ namespace Dalamud.Injector
|
|||
targetProcessSpecified = true;
|
||||
processes.AddRange(Process.GetProcessesByName("ffxiv_dx11"));
|
||||
}
|
||||
else if (args[i] == "--fix-acl" || args[i] == "--acl-fix")
|
||||
{
|
||||
tryFixAcl = true;
|
||||
}
|
||||
else if (args[i] == "--se-debug-privilege")
|
||||
{
|
||||
tryClaimSeDebugPrivilege = true;
|
||||
}
|
||||
else if (args[i] == "--warn")
|
||||
{
|
||||
warnManualInjection = true;
|
||||
|
|
@ -416,8 +427,21 @@ namespace Dalamud.Injector
|
|||
}
|
||||
}
|
||||
|
||||
if (tryClaimSeDebugPrivilege)
|
||||
{
|
||||
try
|
||||
{
|
||||
NativeAclFix.ClaimSeDebug();
|
||||
Log.Information("SeDebugPrivilege claimed.");
|
||||
}
|
||||
catch (Win32Exception e2)
|
||||
{
|
||||
Log.Warning(e2, "Failed to claim SeDebugPrivilege");
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var process in processes)
|
||||
Inject(process, AdjustStartInfo(dalamudStartInfo, process.MainModule.FileName));
|
||||
Inject(process, AdjustStartInfo(dalamudStartInfo, process.MainModule.FileName), tryFixAcl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -431,6 +455,7 @@ namespace Dalamud.Injector
|
|||
var showHelp = args.Count <= 2;
|
||||
var handleOwner = IntPtr.Zero;
|
||||
var withoutDalamud = false;
|
||||
var noFixAcl = false;
|
||||
|
||||
var parsingGameArgument = false;
|
||||
for (var i = 2; i < args.Count; i++)
|
||||
|
|
@ -447,6 +472,8 @@ namespace Dalamud.Injector
|
|||
useFakeArguments = true;
|
||||
else if (args[i] == "--without-dalamud")
|
||||
withoutDalamud = true;
|
||||
else if (args[i] == "--no-fix-acl" || args[i] == "--no-acl-fix")
|
||||
noFixAcl = true;
|
||||
else if (args[i] == "-g")
|
||||
gamePath = args[++i];
|
||||
else if (args[i].StartsWith("--game="))
|
||||
|
|
@ -547,7 +574,7 @@ namespace Dalamud.Injector
|
|||
}
|
||||
|
||||
var gameArgumentString = string.Join(" ", gameArguments.Select(x => EncodeParameterArgument(x)));
|
||||
var process = NativeAclFix.LaunchGame(Path.GetDirectoryName(gamePath), gamePath, gameArgumentString, (Process p) =>
|
||||
var process = NativeAclFix.LaunchGame(Path.GetDirectoryName(gamePath), gamePath, gameArgumentString, noFixAcl, (Process p) =>
|
||||
{
|
||||
if (!withoutDalamud && mode == "entrypoint")
|
||||
{
|
||||
|
|
@ -565,7 +592,7 @@ namespace Dalamud.Injector
|
|||
{
|
||||
var startInfo = AdjustStartInfo(dalamudStartInfo, gamePath);
|
||||
Log.Information("Using start info: {0}", JsonConvert.SerializeObject(startInfo));
|
||||
Inject(process, startInfo);
|
||||
Inject(process, startInfo, false);
|
||||
}
|
||||
|
||||
var processHandleForOwner = IntPtr.Zero;
|
||||
|
|
@ -647,8 +674,20 @@ namespace Dalamud.Injector
|
|||
};
|
||||
}
|
||||
|
||||
private static void Inject(Process process, DalamudStartInfo startInfo)
|
||||
private static void Inject(Process process, DalamudStartInfo startInfo, bool tryFixAcl = false)
|
||||
{
|
||||
if (tryFixAcl)
|
||||
{
|
||||
try
|
||||
{
|
||||
NativeAclFix.CopyAclFromSelfToTargetProcess(process.SafeHandle.DangerousGetHandle());
|
||||
}
|
||||
catch (Win32Exception e1)
|
||||
{
|
||||
Log.Warning(e1, "Failed to copy ACL");
|
||||
}
|
||||
}
|
||||
|
||||
var bootName = "Dalamud.Boot.dll";
|
||||
var bootPath = Path.GetFullPath(bootName);
|
||||
|
||||
|
|
|
|||
|
|
@ -22,41 +22,46 @@ namespace Dalamud.Injector
|
|||
/// <param name="workingDir">The working directory.</param>
|
||||
/// <param name="exePath">The path to the executable file.</param>
|
||||
/// <param name="arguments">Arguments to pass to the executable file.</param>
|
||||
/// <param name="dontFixAcl">Don't actually fix the ACL.</param>
|
||||
/// <param name="beforeResume">Action to execute before the process is started.</param>
|
||||
/// <returns>The started process.</returns>
|
||||
/// <exception cref="Win32Exception">Thrown when a win32 error occurs.</exception>
|
||||
/// <exception cref="GameExitedException">Thrown when the process did not start correctly.</exception>
|
||||
public static Process LaunchGame(string workingDir, string exePath, string arguments, Action<Process> beforeResume)
|
||||
public static Process LaunchGame(string workingDir, string exePath, string arguments, bool dontFixAcl, Action<Process> beforeResume)
|
||||
{
|
||||
Process process = null;
|
||||
|
||||
var userName = Environment.UserName;
|
||||
|
||||
var pExplicitAccess = default(PInvoke.EXPLICIT_ACCESS);
|
||||
PInvoke.BuildExplicitAccessWithName(
|
||||
ref pExplicitAccess,
|
||||
userName,
|
||||
PInvoke.STANDARD_RIGHTS_ALL | PInvoke.SPECIFIC_RIGHTS_ALL & ~PInvoke.PROCESS_VM_WRITE,
|
||||
PInvoke.GRANT_ACCESS,
|
||||
0);
|
||||
|
||||
if (PInvoke.SetEntriesInAcl(1, ref pExplicitAccess, IntPtr.Zero, out var newAcl) != 0)
|
||||
|
||||
var psecDesc = IntPtr.Zero;
|
||||
if (!dontFixAcl)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
var userName = Environment.UserName;
|
||||
|
||||
if (!PInvoke.InitializeSecurityDescriptor(out var secDesc, PInvoke.SECURITY_DESCRIPTOR_REVISION))
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
var pExplicitAccess = default(PInvoke.EXPLICIT_ACCESS);
|
||||
PInvoke.BuildExplicitAccessWithName(
|
||||
ref pExplicitAccess,
|
||||
userName,
|
||||
PInvoke.STANDARD_RIGHTS_ALL | PInvoke.SPECIFIC_RIGHTS_ALL & ~PInvoke.PROCESS_VM_WRITE,
|
||||
PInvoke.GRANT_ACCESS,
|
||||
0);
|
||||
|
||||
if (!PInvoke.SetSecurityDescriptorDacl(ref secDesc, true, newAcl, false))
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
if (PInvoke.SetEntriesInAcl(1, ref pExplicitAccess, IntPtr.Zero, out var newAcl) != 0)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
var psecDesc = Marshal.AllocHGlobal(Marshal.SizeOf<PInvoke.SECURITY_DESCRIPTOR>());
|
||||
Marshal.StructureToPtr(secDesc, psecDesc, true);
|
||||
if (!PInvoke.InitializeSecurityDescriptor(out var secDesc, PInvoke.SECURITY_DESCRIPTOR_REVISION))
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
if (!PInvoke.SetSecurityDescriptorDacl(ref secDesc, true, newAcl, false))
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
psecDesc = Marshal.AllocHGlobal(Marshal.SizeOf<PInvoke.SECURITY_DESCRIPTOR>());
|
||||
Marshal.StructureToPtr(secDesc, psecDesc, true);
|
||||
}
|
||||
|
||||
var lpProcessInformation = default(PInvoke.PROCESS_INFORMATION);
|
||||
try
|
||||
|
|
@ -109,7 +114,8 @@ namespace Dalamud.Injector
|
|||
Environment.SetEnvironmentVariable("__COMPAT_LAYER", compatLayerPrev);
|
||||
}
|
||||
|
||||
DisableSeDebug(lpProcessInformation.hProcess);
|
||||
if (!dontFixAcl)
|
||||
DisableSeDebug(lpProcessInformation.hProcess);
|
||||
|
||||
process = new ExistingProcess(lpProcessInformation.hProcess);
|
||||
|
||||
|
|
@ -133,30 +139,8 @@ namespace Dalamud.Injector
|
|||
throw new GameExitedException();
|
||||
}
|
||||
|
||||
if (PInvoke.GetSecurityInfo(
|
||||
PInvoke.GetCurrentProcess(),
|
||||
PInvoke.SE_OBJECT_TYPE.SE_KERNEL_OBJECT,
|
||||
PInvoke.SECURITY_INFORMATION.DACL_SECURITY_INFORMATION,
|
||||
IntPtr.Zero,
|
||||
IntPtr.Zero,
|
||||
out var pACL,
|
||||
IntPtr.Zero,
|
||||
IntPtr.Zero) != 0)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
if (PInvoke.SetSecurityInfo(
|
||||
lpProcessInformation.hProcess,
|
||||
PInvoke.SE_OBJECT_TYPE.SE_KERNEL_OBJECT,
|
||||
PInvoke.SECURITY_INFORMATION.DACL_SECURITY_INFORMATION | PInvoke.SECURITY_INFORMATION.UNPROTECTED_DACL_SECURITY_INFORMATION,
|
||||
IntPtr.Zero,
|
||||
IntPtr.Zero,
|
||||
pACL,
|
||||
IntPtr.Zero) != 0)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
if (!dontFixAcl)
|
||||
CopyAclFromSelfToTargetProcess(lpProcessInformation.hProcess);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -175,13 +159,91 @@ namespace Dalamud.Injector
|
|||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeHGlobal(psecDesc);
|
||||
if (psecDesc != IntPtr.Zero)
|
||||
Marshal.FreeHGlobal(psecDesc);
|
||||
PInvoke.CloseHandle(lpProcessInformation.hThread);
|
||||
}
|
||||
|
||||
return process;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies ACL of current process to the target process.
|
||||
/// </summary>
|
||||
/// <param name="hProcess">Native handle to the target process.</param>
|
||||
/// <exception cref="Win32Exception">Thrown when a win32 error occurs.</exception>
|
||||
public static void CopyAclFromSelfToTargetProcess(IntPtr hProcess)
|
||||
{
|
||||
if (PInvoke.GetSecurityInfo(
|
||||
PInvoke.GetCurrentProcess(),
|
||||
PInvoke.SE_OBJECT_TYPE.SE_KERNEL_OBJECT,
|
||||
PInvoke.SECURITY_INFORMATION.DACL_SECURITY_INFORMATION,
|
||||
IntPtr.Zero,
|
||||
IntPtr.Zero,
|
||||
out var pACL,
|
||||
IntPtr.Zero,
|
||||
IntPtr.Zero) != 0)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
if (PInvoke.SetSecurityInfo(
|
||||
hProcess,
|
||||
PInvoke.SE_OBJECT_TYPE.SE_KERNEL_OBJECT,
|
||||
PInvoke.SECURITY_INFORMATION.DACL_SECURITY_INFORMATION | PInvoke.SECURITY_INFORMATION.UNPROTECTED_DACL_SECURITY_INFORMATION,
|
||||
IntPtr.Zero,
|
||||
IntPtr.Zero,
|
||||
pACL,
|
||||
IntPtr.Zero) != 0)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
}
|
||||
|
||||
public static void ClaimSeDebug()
|
||||
{
|
||||
var hToken = PInvoke.INVALID_HANDLE_VALUE;
|
||||
try
|
||||
{
|
||||
if (!PInvoke.OpenThreadToken(PInvoke.GetCurrentThread(), PInvoke.TOKEN_QUERY | PInvoke.TOKEN_ADJUST_PRIVILEGES, false, out hToken))
|
||||
{
|
||||
if (Marshal.GetLastWin32Error() != PInvoke.ERROR_NO_TOKEN)
|
||||
throw new Exception("ClaimSeDebug.OpenProcessToken#1", new Win32Exception(Marshal.GetLastWin32Error()));
|
||||
|
||||
if (!PInvoke.ImpersonateSelf(PInvoke.SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation))
|
||||
throw new Exception("ClaimSeDebug.ImpersonateSelf", new Win32Exception(Marshal.GetLastWin32Error()));
|
||||
|
||||
if (!PInvoke.OpenThreadToken(PInvoke.GetCurrentThread(), PInvoke.TOKEN_QUERY | PInvoke.TOKEN_ADJUST_PRIVILEGES, false, out hToken))
|
||||
throw new Exception("ClaimSeDebug.OpenProcessToken#2", new Win32Exception(Marshal.GetLastWin32Error()));
|
||||
}
|
||||
|
||||
var luidDebugPrivilege = default(PInvoke.LUID);
|
||||
if (!PInvoke.LookupPrivilegeValue(null, PInvoke.SE_DEBUG_NAME, ref luidDebugPrivilege))
|
||||
throw new Exception("ClaimSeDebug.LookupPrivilegeValue", new Win32Exception(Marshal.GetLastWin32Error()));
|
||||
|
||||
var tpLookup = new PInvoke.TOKEN_PRIVILEGES()
|
||||
{
|
||||
PrivilegeCount = 1,
|
||||
Privileges = new PInvoke.LUID_AND_ATTRIBUTES[1]
|
||||
{
|
||||
new PInvoke.LUID_AND_ATTRIBUTES()
|
||||
{
|
||||
Luid = luidDebugPrivilege,
|
||||
Attributes = PInvoke.SE_PRIVILEGE_ENABLED,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
if (!PInvoke.AdjustTokenPrivileges(hToken, false, ref tpLookup, 0, IntPtr.Zero, IntPtr.Zero))
|
||||
throw new Exception("ClaimSeDebug.AdjustTokenPrivileges", new Win32Exception(Marshal.GetLastWin32Error()));
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (hToken != PInvoke.INVALID_HANDLE_VALUE && hToken != IntPtr.Zero)
|
||||
PInvoke.CloseHandle(hToken);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DisableSeDebug(IntPtr processHandle)
|
||||
{
|
||||
if (!PInvoke.OpenProcessToken(processHandle, PInvoke.TOKEN_QUERY | PInvoke.TOKEN_ADJUST_PRIVILEGES, out var tokenHandle))
|
||||
|
|
@ -190,7 +252,7 @@ namespace Dalamud.Injector
|
|||
}
|
||||
|
||||
var luidDebugPrivilege = default(PInvoke.LUID);
|
||||
if (!PInvoke.LookupPrivilegeValue(null, "SeDebugPrivilege", ref luidDebugPrivilege))
|
||||
if (!PInvoke.LookupPrivilegeValue(null, PInvoke.SE_DEBUG_NAME, ref luidDebugPrivilege))
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
|
@ -222,7 +284,7 @@ namespace Dalamud.Injector
|
|||
tokenPrivileges.Privileges[0].Luid = luidDebugPrivilege;
|
||||
tokenPrivileges.Privileges[0].Attributes = PInvoke.SE_PRIVILEGE_REMOVED;
|
||||
|
||||
if (!PInvoke.AdjustTokenPrivileges(tokenHandle, false, ref tokenPrivileges, 0, IntPtr.Zero, 0))
|
||||
if (!PInvoke.AdjustTokenPrivileges(tokenHandle, false, ref tokenPrivileges, 0, IntPtr.Zero, IntPtr.Zero))
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
|
@ -271,6 +333,10 @@ namespace Dalamud.Injector
|
|||
private static class PInvoke
|
||||
{
|
||||
#region Constants
|
||||
public static readonly IntPtr INVALID_HANDLE_VALUE = new(-1);
|
||||
|
||||
public const string SE_DEBUG_NAME = "SeDebugPrivilege";
|
||||
|
||||
public const UInt32 STANDARD_RIGHTS_ALL = 0x001F0000;
|
||||
public const UInt32 SPECIFIC_RIGHTS_ALL = 0x0000FFFF;
|
||||
public const UInt32 PROCESS_VM_WRITE = 0x0020;
|
||||
|
|
@ -289,6 +355,8 @@ namespace Dalamud.Injector
|
|||
public const UInt32 SE_PRIVILEGE_ENABLED = 0x00000002;
|
||||
public const UInt32 SE_PRIVILEGE_REMOVED = 0x00000004;
|
||||
|
||||
public const UInt32 ERROR_NO_TOKEN = 0x000003F0;
|
||||
|
||||
public enum MULTIPLE_TRUSTEE_OPERATION
|
||||
{
|
||||
NO_MULTIPLE_TRUSTEE,
|
||||
|
|
@ -345,6 +413,14 @@ namespace Dalamud.Injector
|
|||
UNPROTECTED_DACL_SECURITY_INFORMATION = 0x20000000,
|
||||
PROTECTED_SACL_SECURITY_INFORMATION = 0x40000000,
|
||||
}
|
||||
|
||||
public enum SECURITY_IMPERSONATION_LEVEL
|
||||
{
|
||||
SecurityAnonymous,
|
||||
SecurityIdentification,
|
||||
SecurityImpersonation,
|
||||
SecurityDelegation
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
|
@ -395,12 +471,24 @@ namespace Dalamud.Injector
|
|||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern uint ResumeThread(IntPtr hThread);
|
||||
|
||||
[DllImport("advapi32.dll", SetLastError = true)]
|
||||
public static extern bool ImpersonateSelf(
|
||||
SECURITY_IMPERSONATION_LEVEL impersonationLevel
|
||||
);
|
||||
|
||||
[DllImport("advapi32.dll", SetLastError = true)]
|
||||
public static extern bool OpenProcessToken(
|
||||
IntPtr processHandle,
|
||||
UInt32 desiredAccess,
|
||||
out IntPtr tokenHandle);
|
||||
|
||||
[DllImport("advapi32.dll", SetLastError = true)]
|
||||
public static extern bool OpenThreadToken(
|
||||
IntPtr ThreadHandle,
|
||||
uint DesiredAccess,
|
||||
bool OpenAsSelf,
|
||||
out IntPtr TokenHandle);
|
||||
|
||||
[DllImport("advapi32.dll", SetLastError = true)]
|
||||
public static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, ref LUID lpLuid);
|
||||
|
||||
|
|
@ -415,9 +503,9 @@ namespace Dalamud.Injector
|
|||
IntPtr tokenHandle,
|
||||
bool disableAllPrivileges,
|
||||
ref TOKEN_PRIVILEGES newState,
|
||||
UInt32 bufferLengthInBytes,
|
||||
int cbPreviousState,
|
||||
IntPtr previousState,
|
||||
UInt32 returnLengthInBytes);
|
||||
IntPtr cbOutPreviousState);
|
||||
|
||||
[DllImport("advapi32.dll", SetLastError = true)]
|
||||
public static extern uint GetSecurityInfo(
|
||||
|
|
@ -443,6 +531,9 @@ namespace Dalamud.Injector
|
|||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern IntPtr GetCurrentProcess();
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern IntPtr GetCurrentThread();
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr hWndChildAfter, string className, IntPtr windowTitle);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue