Implement xiv fixes into Dalamud.Boot (#857)

This commit is contained in:
kizer 2022-05-29 02:11:03 +09:00 committed by GitHub
parent 02dd1eddec
commit 75de126c9d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 41576 additions and 196 deletions

View file

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

View file

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